Models based on ‘basic’ indicators of engagement and regularity of study

Loading the required data

daily.counts <- read.csv("Intermediate_results/regularity_of_study/weekly_counts_of_daily_logins_w2-13.csv")
#colnames(daily.counts)
weekly.counts <- daily.counts %>%
  select(user_id, W2_cnt:W13_cnt, tot_cnt, weekly_entropy)
# str(weekly.counts)
daily.gaps <- read.csv("Intermediate_results/regularity_of_study/gaps_between_consecutive_logins_w2-13.csv")
# str(daily.gaps)
# daily gaps do not have normal distribution, so, median will be used
# merge weekly counts and median time gap 
counts.data <- merge(x = weekly.counts, y = daily.gaps %>% select(user_id, median_gap),
                     by = 'user_id', all = TRUE)
exam.scores <- read.csv(file = "Intermediate_results/exam_scores_with_student_ids.csv")
# remove email data
exam.scores <- exam.scores %>% select(-2)
# str(exam.scores)
# merge counts data with exam scores
counts.data <- merge(x = counts.data, y = exam.scores, by.x = 'user_id', by.y = 'USER_ID', 
                     all.x = T, all.y = F)
#summary(counts.data)
# 9 NA values for exam scores; remove them
counts.data <- counts.data %>% filter( is.na(SC_FE_TOT)==FALSE )

Model 1: predictors are the same as those used in the latest cluster model

This means that predictors are counts of active days (days when a student had at least one learning session) per week, entropy of weekly active days, and median gap between two consecutive active days.

lm1.data <- counts.data %>% select(-c(tot_cnt, user_id, SC_MT_TOT))
lm1 <- lm(SC_FE_TOT ~ ., data = lm1.data)
summary(lm1)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm1.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-23.2397  -6.3099  -0.6918   5.9104  19.9677 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)      8.2257     2.3354   3.522  0.00047 ***
W2_cnt           0.4985     0.3431   1.453  0.14685    
W3_cnt           0.0926     0.3651   0.254  0.79991    
W4_cnt           0.1341     0.3532   0.380  0.70441    
W5_cnt          -0.3261     0.3228  -1.010  0.31290    
W6_cnt          -0.1632     0.3676  -0.444  0.65736    
W7_cnt           0.5343     0.4188   1.276  0.20266    
W8_cnt           1.0919     0.4068   2.685  0.00752 ** 
W9_cnt           0.1106     0.3862   0.286  0.77475    
W10_cnt          1.0810     0.3454   3.130  0.00186 ** 
W11_cnt          0.1618     0.3891   0.416  0.67768    
W12_cnt          0.4559     0.3298   1.383  0.16746    
W13_cnt          0.8561     0.2901   2.951  0.00333 ** 
weekly_entropy  11.6096     9.9681   1.165  0.24475    
median_gap      -0.2072     0.3777  -0.549  0.58350    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.417 on 462 degrees of freedom
Multiple R-squared:  0.2831,    Adjusted R-squared:  0.2614 
F-statistic: 13.03 on 14 and 462 DF,  p-value: < 2.2e-16

It’s interesting that counts for only 3 weeks are significant and that all three weeks are in the 2nd part of the course (after midterm exam):

  • one day more of study in week 8 contributes 1.09 points to the final exam score;
  • one day more in week 10 controbutes 1.08 points, and
  • one active day more in week 13 adds 0.86 points.

R-squared is 0.283 (adjusted R2: 0.261).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm1$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm1)
par(mfrow=c(1,1)) # Change back to 1 x 1

# there are few potential influential points: 80, 50, 459
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:14)
  print(cor.test(lm1.data[,c], lm1$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm1)
# OK, values below or equal to 2

The assumptions are satisifed, though there are few potentially influential points that might need to be considered if this model is to be used

Model 2: Like Model 1, but instead of weekly counts, uses total number of active days during the course

lm2.data <- counts.data %>% select(tot_cnt, median_gap, weekly_entropy, SC_FE_TOT)
lm2 <- lm(SC_FE_TOT ~ ., data = lm2.data)
summary(lm2)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm2.data)

Residuals:
    Min      1Q  Median      3Q     Max 
-25.649  -6.003  -0.660   5.943  20.407 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     5.84084    2.17939   2.680  0.00762 ** 
tot_cnt         0.40120    0.04530   8.857  < 2e-16 ***
median_gap     -0.05991    0.37409  -0.160  0.87284    
weekly_entropy 11.87966    9.90829   1.199  0.23114    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.476 on 473 degrees of freedom
Multiple R-squared:  0.2557,    Adjusted R-squared:  0.251 
F-statistic: 54.17 on 3 and 473 DF,  p-value: < 2.2e-16

The total number of active days is the only significant predictor, and it is highly significant. Each additional active day contributes 0.4 points to the final exam score.

R-squared is 0.2557 (adjusted R2: 0.251).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm2$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm2)
par(mfrow=c(1,1)) # Change back to 1 x 1

# both OK
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:3)
  print(cor.test(lm2.data[,c], lm2$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm2)
# OK, values below or equal to 2

All assumptions are satisified.

Model 3: Number of study sessions per week, weekly entropy of study session counts, and time gap between consecutive sessions

Loading the required data

weekly.sessions <- read.csv("Intermediate_results/regularity_of_study/weekly_session_props.csv")
#str(weekly.sessions)
ses.gap.data <- read.csv("Intermediate_results/regularity_of_study/inter-session_time_intervals.csv") #str(ses.gap.data)
lm3.data <- merge(x = weekly.sessions %>% select(count_w2:count_w12, weekly_entropy, user_id),
                  y = ses.gap.data %>% select(user_id, median_s_gap),
                  by = 'user_id', all = TRUE)
lm3.data <- merge(x = lm3.data, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
                  by.x = 'user_id', by.y = 'USER_ID', all.x = T, all.y = F)
summary(lm3.data)
## remove rows with NAs
lm3.data <- lm3.data %>% filter(is.na(SC_FE_TOT)==FALSE & is.na(median_s_gap)==FALSE) 
# ## since some of the predictors are on quite different scales, rescale them
# apply(lm3.data %>% select(-user_id), 2, shapiro.test)
# apply(lm3.data %>% select(-user_id), 2, function(x) length(boxplot.stats(x)$out))
# # all preditors have outliers -> normalization is not advised
# lm3.sc.data <- scale.features(lm3.data %>% select(-c(user_id, SC_FE_TOT)))
# lm3.sc.data <- cbind(lm3.sc.data, SC_FE_TOT=lm3.data$SC_FE_TOT)
# summary(lm3.sc.data)
## the same results are obtained with scaled and unscaled (original) data;
## will keep the results with original data as they are easier to interpret
lm3.data <- lm3.data %>% select(-user_id)
lm3 <- lm(SC_FE_TOT ~ ., data = lm3.data)
summary(lm3)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm3.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-21.6292  -5.9597  -0.8594   5.9990  21.2523 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)    15.2754010  1.6511046   9.252  < 2e-16 ***
count_w2        0.0053628  0.1110213   0.048  0.96149    
count_w3        0.1909662  0.1157368   1.650  0.09962 .  
count_w4       -0.0832153  0.1005015  -0.828  0.40810    
count_w5       -0.2802013  0.0866320  -3.234  0.00131 ** 
count_w7        0.2530987  0.1498286   1.689  0.09184 .  
count_w8        0.1688238  0.1441518   1.171  0.24214    
count_w9        0.1951703  0.1603609   1.217  0.22420    
count_w10       0.5158746  0.1080020   4.777  2.4e-06 ***
count_w11       0.3756283  0.1545471   2.431  0.01546 *  
count_w12       0.1057666  0.1333451   0.793  0.42808    
weekly_entropy 25.4435546  8.4577490   3.008  0.00277 ** 
median_s_gap   -0.0001634  0.0003508  -0.466  0.64167    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.31 on 463 degrees of freedom
Multiple R-squared:  0.2938,    Adjusted R-squared:  0.2755 
F-statistic: 16.05 on 12 and 463 DF,  p-value: < 2.2e-16

Significant predictors:

  • session counts in week 5: negative impact (!), an additional session decreases exam score by 0.28
  • session counts in week 10: an additional session increases exam score by 0.52
  • session counts in week 11: an additional session increases exam score by 0.37
  • weekly entropy: if entropy increases by 1, exam score increases by 25; however, entropy cannot increase not nearly that much; this simply confirms that the higher the regularity (high entropy means that weekly counts are almost uniformly distributed), the higher the performance

R-squared: 0.294 (adjusted R2: 0.275).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm3$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm3)
par(mfrow=c(1,1)) # Change back to 1 x 1

# mostly fine, but there are few (potentially) influential points: 412, 459, 437, 77
# let's examine them
lm3.data[c(412,459,437,77),]
summary(lm3.data)
# 437 has very low engagement and very high exam score (35)
# 459 has high engagement (at times very high) and zero (0) exam score
# 412 is similar to 459, but not that extreme (7 exam score; less active)
# 77 is almost completely inactive, and has zero (0) exam score
## assumption 4: predictors and residuals are uncorrelated
lm3.data <- lm3.data %>% filter(is.na(median_s_gap)==FALSE)
for(c in 1:12)
  print(cor.test(lm3.data[,c], lm3$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm3)
# OK, values below or slightly above 2

Model 4: Total number of study sessions, weekly entropy of study session counts, and time gap between consecutive sessions

lm4.data <- merge(x = weekly.sessions %>% select(user_id, s_total, weekly_entropy),
                  y = ses.gap.data %>% select(-mad_s_gap),
                  by = 'user_id', all = TRUE)
lm4.data <- merge(x = lm4.data, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
                  by.x = 'user_id', by.y = 'USER_ID', all.x = T, all.y = F)
lm4.data <- lm4.data %>% filter( is.na(SC_FE_TOT)==FALSE ) %>% select(-user_id)
lm4 <- lm(SC_FE_TOT ~ ., data = lm4.data)
summary(lm4)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm4.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-29.4102  -6.3591  -0.7392   6.5142  19.4481 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    1.526e+01  1.658e+00   9.205  < 2e-16 ***
s_total        1.235e-01  1.383e-02   8.926  < 2e-16 ***
weekly_entropy 3.188e+01  8.598e+00   3.708 0.000233 ***
median_s_gap   2.975e-05  3.607e-04   0.082 0.934310    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.599 on 472 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.2292,    Adjusted R-squared:  0.2243 
F-statistic: 46.78 on 3 and 472 DF,  p-value: < 2.2e-16

Significant predictors:

  • total number of sessions: an additional session increases exam score by 0.12
  • weekly entropy: if entropy increases by 1, exam score increases by 31.88; however, entropy cannot increase not nearly that much; this simply confirms that the higher the regularity (high entropy means that weekly counts are almost uniformly distributed), the higher the performance

R-squared: 0.229 (adjusted R2: 0.224).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm4$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm4)
par(mfrow=c(1, 1))

# the Residuals vs Fitted plot suggests that there might be some non-linear realtionship between the outcome and the predictors
# there are also few influential points: 412, 459, 376, 77
# let's examine them
lm4.data[c(412,459,376, 77),]
summary(lm4.data)
# 77 is a clear outlier
# 376 has relatively high engagement (above 3rd quartile), but very low exam score (4)
# 412 and 459 have already been examined before
## assumption 4: predictors and residuals are uncorrelated
lm4.data <- lm4.data %>% filter(is.na(median_s_gap)==FALSE)
for(c in 1:3)
  print(cor.test(lm4.data[,c], lm4$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm4)
# OK, values below 2

Model 5: Number of study sessions per week day, and week day entropy of study session counts

Loading the data

weekday.sessions <- read.csv("Intermediate_results/regularity_of_study/weekday_session_props.csv")
#str(weekday.sessions)
lm5.data <- merge(x = weekday.sessions %>% select(1:8, 11),
                  y = exam.scores %>% select(-SC_MT_TOT),
                  by.x = "user_id", by.y = "USER_ID",
                  all.x = TRUE, all.y = FALSE)
# summary(lm5.data)
lm5.data <- lm5.data %>% filter( is.na(SC_FE_TOT)==FALSE ) %>% select(-user_id)
lm5 <- lm(SC_FE_TOT ~ ., data = lm5.data)
summary(lm5)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm5.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-31.2357  -6.1439  -0.9892   6.7715  20.2779 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     15.07048    2.26948   6.641 8.69e-11 ***
Sun_count        0.08458    0.09785   0.864 0.387818    
Mon_count        0.20397    0.04877   4.182 3.44e-05 ***
Tue_count        0.11681    0.03721   3.140 0.001799 ** 
Wed_count        0.10067    0.04187   2.405 0.016578 *  
Thu_count        0.16141    0.04133   3.905 0.000108 ***
Fri_count        0.12135    0.10226   1.187 0.235970    
Sat_count       -0.02262    0.12568  -0.180 0.857269    
weekday_entropy 17.61256    6.86108   2.567 0.010567 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.663 on 468 degrees of freedom
Multiple R-squared:  0.2307,    Adjusted R-squared:  0.2176 
F-statistic: 17.55 on 8 and 468 DF,  p-value: < 2.2e-16

Significant predictors:

  • Monday session counts: an additional Mon session increases exam score by 0.204
  • Tuesday session counts: an additional Tue session increases exam score by 0.117
  • Wednesday session counts: an additional Wed session increases exam score by 0.1
  • Thursday session counts: an additional Thu session increases exam score by 0.161
  • weekly entropy: if entropy increases by 1, exam score increases by 17.6; however, entropy cannot increase not nearly that much; this simply confirms that the higher the regularity (high entropy means that weekly counts are almost uniformly distributed), the higher the performance

R-squared: 0.231 (adjusted R2: 0.218).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm5$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm5)
par(mfrow=c(1,1)) # Change back to 1 x 1

# mostly fine, but there are few potentially influential points: usual suspects (412, 459), 230
# let's examine them
lm5.data[c(412,459,230),]
summary(lm5.data)
# 230 has low engagement and very high exam score (35)
# 459 and 412 have already been considered
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:8)
  print(cor.test(lm5.data[,c], lm5$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm5)
# OK, values below 2

Models based on ‘advanced’ indicators of engagement and regularity of study

Model 6: Daily resource use

As predictors, use total counts of different kinds of resources students used during their active days (an active day is a day when a student had at least one study session). The types of resources considered:

  • video (VIDEO)
  • exercises (EXE)
  • multiple choice questions (MCQ)
  • reading materials (RES)
  • metacognitive items (METACOG)

In addition, consider using:

  • median_X_cnt - median number of resources of type X used during the student’s active days (X can be VIDEO, EXE, MCQ, RES, METACOG)
  • mad_X_cnt - MAD (median absolute deviation) of resources of the type X used during the student’s active days
  • days_X_used - number of days when resources of the type X were used
  • prop_X_used - proportion of days when resources of the type X were used versus total number of the student’s active days

Loading the data…

res.use.stats <- read.csv("Intermediate_results/regularity_of_study/daily_resource_use_statistics_w2-5_7-12.csv")
#str(res.use.stats)
lm6.data <- merge(res.use.stats, exam.scores, by.x = "user_id", by.y = "USER_ID", all.x = T, all.y = F)
lm6.data <- lm6.data %>% select(-c(user_id, SC_MT_TOT)) %>% filter( is.na(SC_FE_TOT)==FALSE )

First, include only the indicators of engagement (not regularity)

lm6_1.data <- lm6.data %>% select( starts_with("tot"), starts_with("prop"), SC_FE_TOT)
# examine the presence of (high) correlation between the variables
ggcorr(lm6_1.data, method = c("complete","spearman"), 
       #      geom = "circle", min_size = 0, max_size = 15,
       label = TRUE, label_size = 3.5,
       hjust = 0.85, size = 4, layout.exp = 1)

# tot_mcog_cnt and prop_mcog_used are highly correlated, as are tot_video_cnt and prop_video_used, and tot_mcq_cnt and prop_mcq_used 
lm6_1.data <- lm6_1.data %>% select(-c(prop_mcog_used, prop_video_used, prop_mcq_used))
# remove the outliers and re-run the model
lm6_1.data <- lm6_1.data[-c(86, 412, 462, 459),]
lm6_1 <- lm(SC_FE_TOT ~., data = lm6_1.data)
summary(lm6_1)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm6_1.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-22.0996  -5.9578  -0.1087   6.5627  20.6982 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    6.505e+01  2.572e+01   2.529   0.0118 *  
tot_video_cnt  1.314e-03  8.603e-04   1.528   0.1273    
tot_exe_cnt   -6.820e-03  1.328e-03  -5.135 4.16e-07 ***
tot_mcq_cnt    8.755e-03  3.661e-03   2.392   0.0172 *  
tot_mcog_cnt   9.811e-03  1.281e-02   0.766   0.4441    
tot_res_cnt    1.008e-02  1.955e-03   5.156 3.74e-07 ***
prop_exe_used  4.138e-01  3.661e+00   0.113   0.9100    
prop_res_used -4.626e+01  2.576e+01  -1.796   0.0732 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.798 on 465 degrees of freedom
Multiple R-squared:  0.1922,    Adjusted R-squared:  0.1801 
F-statistic: 15.81 on 7 and 465 DF,  p-value: < 2.2e-16

Significant predictors:

  • total exercise counts (number of exercise-related events during the student’s active days): an additional exercise-related event decreases the final exam score by 0.0066
  • total MCQ counts (number of MCQ-related events during the student’s active days): an additional MCQ-related event increases the final exam score by 0.0078
  • total number of reading related events: an additional reading-related event decreases the final exam score by 0.0096

R-squared: 0.192 (adjusted R2: 0.180).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm6_1$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm6_1)
par(mfrow=c(1, 1))

# unclear if homoscedasticity requirement is fulfilled; check using this plot:
check.homoschedasticity(lm6_1)

# not that good
# # the plots point to couple of outliers: 86, 412, 462, 459 
# # let's check them:
# lm6_1.data[c(86, 412, 462, 459),]
# summary(lm6_1.data)
# # 459 and 462 have zero exam score, inspite of non-negligible number of learning events (especially 459)
# # 412 was highly active, but had very low exam score (7)
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:7)
  print(cor.test(lm6_1.data[,c], lm6_1$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm6_1)
# it's fine: all below or equal to 2

The assumption of homoscedasticity cannot be considered satisfied (even after removing outliers)

Now, include both indicators of engagement and indicator of regularity

# include those engagment indicators that proved at least slightly relevant in the previous model
# plus mad_X_cnt as indicators of regularity
lm6_2.data <- lm6.data %>% select(tot_mcq_cnt, tot_exe_cnt, tot_res_cnt, prop_res_used, 
                                  starts_with("mad"), SC_FE_TOT)
# examine the presence of (high) correlation between the variables
plot.correlations(lm6_2.data)

# exclude mad_res_cnt as highly correlated with tot_res_cnt (which proved significant)
lm6_2.data <- lm6_2.data %>% select(-mad_res_cnt)
lm6_2 <- lm(SC_FE_TOT ~., data = lm6_2.data)
summary(lm6_2)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm6_2.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-22.9335  -6.1144  -0.1174   6.8471  23.1086 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    66.127636  26.170686   2.527 0.011840 *  
tot_mcq_cnt     0.012136   0.003640   3.334 0.000923 ***
tot_exe_cnt    -0.006543   0.001360  -4.813 2.01e-06 ***
tot_res_cnt     0.010416   0.002000   5.208 2.87e-07 ***
prop_res_used -47.054649  26.296683  -1.789 0.074201 .  
mad_video_cnt  -0.007622   0.081861  -0.093 0.925861    
mad_exe_cnt    -0.003117   0.020326  -0.153 0.878197    
mad_mcq_cnt    -0.543694   0.379258  -1.434 0.152362    
mad_mcog_cnt   -0.952299   1.089663  -0.874 0.382599    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.983 on 468 degrees of freedom
Multiple R-squared:  0.1728,    Adjusted R-squared:  0.1586 
F-statistic: 12.22 on 8 and 468 DF,  p-value: 6.383e-16

None of the MAD variables is significant

Model 7: Daily topic focus

As predictors, use total number of learning actions (during active days) with a particular topic focus; possible topic foci:

  • ‘ontopic’ - the topic associated with the action is the topic of the current week
  • ‘revisiting’ - the topic associated with the action is the topic of one of the previous weeks
  • ‘metacognitive’ - the topic associated with the action is one of the following: ‘ORG’, ‘DBOARD’, ‘STRAT’, ‘STUDYKIT’
  • ‘orienteering’ - the topic associated with the action is one of the following: ‘HOME’, ‘HOF’, ‘SEARCH’, ‘TEA’, ‘EXAM’, ‘W01’-‘W13’
  • ‘project’ - the action is related to project work

In addition, consider including the following basic statistics:

  • median_X_cnt - median number of learning actions per active day with a particular topic focus
  • mad_X_cnt - MAD of learning actions per active day with a particular topic focus
  • X_days - number of days with at least one action with particular topic focus
  • X_prop - proportion of days with the given type of topic focus versus total number of active days

Loading the required data…

topic.stats <- read.csv("Intermediate_results/regularity_of_study/topic_counts_statistics_w2-5_7-12.csv")
# str(topic.stats)
lm7.data <- merge(topic.stats, exam.scores, by.x = "user_id", by.y = "USER_ID", all.x = T, all.y = F)
lm7.data <- lm7.data %>% select(-c(user_id, SC_MT_TOT)) %>% filter( is.na(SC_FE_TOT)==FALSE )

First, include only the indicators of engagement (not regularity)

lm7_1.data <- lm7.data %>% select( starts_with("tot"), ends_with("prop"), SC_FE_TOT)
summary(lm7_1.data)
# examine the presence of (high) correlation between the variables
plot.correlations(lm7_1.data)

# exclude tot_orient_cnt and orinet_prop as they are highly correlated with some other variables
lm7_1.data <- lm7_1.data %>% select(-c(tot_orient_cnt, orient_prop))
# exclude tot_prj_cnt, due to high VIF
lm7_1.data <- lm7_1.data %>% select(-tot_prj_cnt)
lm7_1 <- lm(SC_FE_TOT ~ ., data = lm7_1.data)
summary(lm7_1)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm7_1.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-22.4947  -6.5546  -0.6803   6.5096  21.4971 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      4.592e+01  1.040e+01   4.413 1.27e-05 ***
tot_ontopic_cnt  4.854e-03  9.985e-04   4.861 1.60e-06 ***
tot_revisit_cnt -5.237e-03  1.455e-03  -3.600 0.000352 ***
tot_metacog_cnt  1.191e-02  3.565e-03   3.341 0.000901 ***
ontopic_prop     8.082e-01  3.322e+00   0.243 0.807890    
revisit_prop     1.940e+00  3.106e+00   0.625 0.532537    
metacog_prop    -3.469e+01  1.017e+01  -3.410 0.000705 ***
prj_prop         4.500e+00  4.893e+00   0.920 0.358177    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 9.063 on 469 degrees of freedom
Multiple R-squared:  0.1562,    Adjusted R-squared:  0.1436 
F-statistic:  12.4 on 7 and 469 DF,  p-value: 1.347e-14

Significant predictors:

  • total number of prepartion (ontopic) events: an additional ontopic event increases the final exam score by 0.0048
  • total number of revisiting events: an additional revisting event decreases the final exam score by 0.0052
  • total number of metacognitive events: an additional metacognitive event increases the final exam score by 0.012
  • proportion of days with metacognitive events versus total number of active days: increase in this proportion decreases the exam score

R-squared: 0.156 (adjusted R2: 0.145).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm7_1$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm7_1)
par(mfrow=c(1, 1))

# normality is fine
# unclear if homoscedasticity requirement is fulfilled; check using this plot:
check.homoschedasticity(lm7_1)

# it's fine
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:8)
  print(cor.test(lm7_1.data[,c], lm7_1$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm7_1)
# now, it's fine

Now, include both indicators of engagement and indicator of regularity

# include those engagment indicators that proved at least slightly relevant in the previous model
# plus mad_X_cnt as indicators of regularity
lm7_2.data <- lm7.data %>% select(tot_ontopic_cnt, tot_revisit_cnt, tot_metacog_cnt, metacog_prop, 
                                  starts_with("mad"), SC_FE_TOT)
# examine the presence of (high) correlation between the variables
plot.correlations(lm7_2.data)

# exclude tot_metacog_cnt as highly correlated with mad_metacog_cnt and mad_orient_cnt
lm7_2.data <- lm7_2.data %>% select(-c(tot_metacog_cnt, mad_orient_cnt))
lm7_2 <- lm(SC_FE_TOT ~., data = lm7_2.data)
summary(lm7_2)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm7_2.data)

Residuals:
    Min      1Q  Median      3Q     Max 
-20.586  -6.413  -1.047   6.841  22.230 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      4.254e+01  9.826e+00   4.330 1.83e-05 ***
tot_ontopic_cnt  7.177e-03  9.323e-04   7.698 8.27e-14 ***
tot_revisit_cnt -3.802e-03  1.352e-03  -2.812  0.00513 ** 
metacog_prop    -2.679e+01  1.019e+01  -2.629  0.00885 ** 
mad_ontopic_cnt -1.257e-01  4.010e-02  -3.135  0.00182 ** 
mad_revisit_cnt -9.064e-02  7.364e-02  -1.231  0.21897    
mad_metacog_cnt -3.913e-02  1.476e-01  -0.265  0.79102    
mad_prj_cnt     -7.659e-01  4.124e+00  -0.186  0.85276    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 9.1 on 469 degrees of freedom
Multiple R-squared:  0.1493,    Adjusted R-squared:  0.1366 
F-statistic: 11.76 on 7 and 469 DF,  p-value: 8.013e-14

The only regularity indicator that proved significant: mad_ontopic_cnt - one unit increase in MAD of ontopic counts leads to a decrease of 0.126 points in the final exam score

R2: 0.1493 (adjusted R2: 0.1366).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm7_2$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm7_2)
par(mfrow=c(1, 1))

# normality is fine
# unclear if homoscedasticity requirement is fulfilled; check using this plot:
check.homoschedasticity(lm7_2)

# not bad
# a few influential points: 60, 54, 202
# and a few outliers: 19, 294, 50
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:7)
  print(cor.test(lm7_2.data[,c], lm7_2$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm7_2)
# OK

A few outliers and (potentially) influential points; apart from that, it’s fine

Model 8: Weekly resource use indicators

Indicators are computed at the week level, based on the following principle: a score of one is given to a student (for a given week), if he/she used certain kind of resource (e.g. video) more than the average (median) use of the that resource type in the given week

Loading the data…

res.use.ind <- read.csv("Intermediate_results/regularity_of_study/res_use_indicators_w2-13.csv")
#str(res.use.ind)
lm8.data <- merge(x = res.use.ind, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
                  by.x = "user_id", by.y = "USER_ID", all.x = TRUE, all.y = FALSE)
#summary(lm8.data)
# remove students who do not have final exam score
lm8.data <- lm8.data %>% filter( is.na(SC_FE_TOT)==FALSE )
lm8.data <- lm8.data %>% select(-user_id)
# examime correlations
plot.correlations(lm8.data)

# video_ind and MCQ_ind are highly correlated, remove one of them
lm8.data <- lm8.data %>% select(-VIDEO_ind)
lm8 <- lm(SC_FE_TOT ~ ., data = lm8.data)
summary(lm8)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm8.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-22.7333  -5.8524  -0.1573   6.1593  21.4361 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 16.10045    1.07114  15.031  < 2e-16 ***
MCQ_ind      0.95088    0.16395   5.800 1.22e-08 ***
EXE_ind     -0.82529    0.14298  -5.772 1.42e-08 ***
RES_ind      0.60658    0.14833   4.089 5.08e-05 ***
METACOG_ind  0.03796    0.15725   0.241    0.809    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.38 on 472 degrees of freedom
Multiple R-squared:  0.2739,    Adjusted R-squared:  0.2678 
F-statistic: 44.52 on 4 and 472 DF,  p-value: < 2.2e-16

Significant predictors:

  • MCQ_ind - a unit increase in this indicator (ie, one week more when a student’s use of MCQs is higher than the average (median) use of MCQ in that week), increases the final exam score by 0.951 points
  • EXE_ind - a unit increase of this indicator (ie, one week more when a student’s use of exercises is higher than the average (median) use of exercises in that week), decreases the exam score by 0.825 points
  • RES_ind - a unit increase of this indicator (ie, one week more when a student’s use of reading materias is higher than the average (median) use of reading content in that week), increases the exam score by 0.606 points.

R-squared: 0.274 (adjusted R2: 0.268).

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm8$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm8)
par(mfrow=c(1, 1))

# both normality and homoscedasticity requirements are fulfilled
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:4)
  print(cor.test(lm8.data[,c], lm8$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm8)
# it's fine

Model 9: Topic focus indicators

Indicators are computed at the week level, based on the following principle: a score of one is given to a student (for a given week), if his/her number of events related to a particular topic type (e.g. revisiting) was above the average (median) number of events with that topic type in the given week

Weeks 6 and 13 are excluded from these computations, as during these weeks one can expect different behavioral patterns than usual.

Loading the data

topic.ind <- read.csv("Intermediate_results/regularity_of_study/topic_based_indicators_w2-5_7-12.csv")
#str(topic.ind)
lm9.data <- merge(x = topic.ind, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
                  by.x = "user_id", by.y = "USER_ID", all.x = TRUE, all.y = FALSE)
#summary(lm9.data)
# remove students who do not have final exam score
lm9.data <- lm9.data %>% filter( is.na(SC_FE_TOT)==FALSE )
lm9.data <- lm9.data %>% select(-user_id)
plot.correlations(lm9.data)

# orient_ind and metacog_ind are highly correlated, remove one of them
lm9.data <- lm9.data %>% select(-orient_ind)
lm9 <- lm(SC_FE_TOT ~ ., data = lm9.data)
summary(lm9)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm9.data)

Residuals:
     Min       1Q   Median       3Q      Max 
-21.7208  -6.8879  -0.8953   7.0348  23.7086 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  13.2952     1.0797  12.314  < 2e-16 ***
ontopic_ind   0.9303     0.1693   5.496 6.38e-08 ***
revisit_ind  -0.2917     0.1714  -1.702   0.0893 .  
metacog_ind   0.4606     0.2082   2.212   0.0274 *  
prj_ind       0.2421     0.3127   0.774   0.4392    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 9.076 on 472 degrees of freedom
Multiple R-squared:  0.1484,    Adjusted R-squared:  0.1412 
F-statistic: 20.56 on 4 and 472 DF,  p-value: 1.236e-15

Significant predictors:

  • ontopic_ind - a unit increase in this indicator (ie, one week more when a student’s number of ‘ontopic’ events is higher than the average (median) number of ‘ontopic’ events in that week), increases the final exam score by 0.9303 points
  • metacog_ind - a unit increase in this indicator (ie, one week more when a student’s number of ‘metacognitive’ events is higher than the average (median) number of ‘metacognitive’ events in that week), increases the final exam score by 0.4606 points

R-squared: 0.1484 (adjusted R-squared: 0.1412)

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm9$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm9)
par(mfrow=c(1, 1))

# both normality and homoscedasticity requirements are fulfilled
# there are few outliers (202, 213, 365), but no influential points
# check the outliers
lm9.data[c(202,213,365),]
# very interesting:
# - 202 was highly active but ended up with zero final exam score
# - 213 and 365 were not preparing for lectures, and generaly had low engagement, but did the exam excellently
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:4)
  print(cor.test(lm9.data[,c], lm9$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm9)
# it's fine

Model 10: Combine resource use and topic focus indicators

Use those indicators that proved significant in the previous two models (models 8 and 9)

Loading the data

topic.ind <- read.csv("Intermediate_results/regularity_of_study/topic_based_indicators_w2-5_7-12.csv")
res.use.ind <- read.csv("Intermediate_results/regularity_of_study/res_use_indicators_w2-13.csv")
lm10.data <- merge(x = topic.ind %>% select(user_id, ontopic_ind, metacog_ind), 
                   y = res.use.ind %>% select(user_id, MCQ_ind, RES_ind, EXE_ind),
                  by = "user_id", all = TRUE)
lm10.data <- merge(x = lm10.data, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
                  by.x = "user_id", by.y = "USER_ID", all.x = TRUE, all.y = FALSE)
summary(lm10.data)
# remove students who do not have final exam score
lm10.data <- lm10.data %>% filter( is.na(SC_FE_TOT)==FALSE )
plot.correlations(lm10.data %>% select(-user_id))

# RES_ind and metacog_ind are highly correlated, remove metacog_ind as it was less significant in the previous models
lm10.data <- lm10.data %>% select(-metacog_ind)
lm10 <- lm(SC_FE_TOT ~ ., data = lm10.data %>% select(-user_id))
summary(lm10)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm10.data %>% select(-user_id))

Residuals:
     Min       1Q   Median       3Q      Max 
-21.6264  -5.5145  -0.4311   6.2478  22.6760 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  16.0064     1.0433  15.343  < 2e-16 ***
ontopic_ind   0.4506     0.2055   2.192 0.028843 *  
MCQ_ind       0.7235     0.1851   3.908 0.000107 ***
RES_ind       0.5582     0.1492   3.742 0.000205 ***
EXE_ind      -0.9275     0.1496  -6.199 1.24e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.339 on 472 degrees of freedom
Multiple R-squared:  0.2812,    Adjusted R-squared:  0.2751 
F-statistic: 46.15 on 4 and 472 DF,  p-value: < 2.2e-16

All 4 predictors are significant; however, only slight improvement in R2 w.r.t. model 8: R-squared: 0.2812 (adjusted R-squared: 0.2751)

Model 11: Extend model 10 with total number of study sessions and entropy of weekly study session counts

Loading the data

weekly.sessions <- read.csv("Intermediate_results/regularity_of_study/weekly_session_props.csv")
lm11.data <- merge(x = lm10.data, y = weekly.sessions %>% select(user_id, s_total, weekly_entropy),
                   by = 'user_id', all.x = TRUE, all.y = FALSE)
#summary(lm11.data)
lm11.data <- lm11.data[,c(1:5,7,8,6)]
plot.correlations(lm11.data %>% select(-user_id))

# total number of sessions is highly correlated with almost all other variables
lm11.data <- lm11.data %>% select(-s_total)
lm11 <- lm(SC_FE_TOT ~ ., data = lm11.data %>% select(-c(user_id, ontopic_ind)))
summary(lm11)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm11.data %>% select(-c(user_id, 
    ontopic_ind)))

Residuals:
     Min       1Q   Median       3Q      Max 
-24.1085  -5.7500  -0.1444   5.9306  20.1971 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     23.6142     1.5988  14.770  < 2e-16 ***
MCQ_ind          0.8916     0.1438   6.200 1.23e-09 ***
RES_ind          0.4795     0.1444   3.320 0.000969 ***
EXE_ind         -0.9923     0.1406  -7.060 6.00e-12 ***
weekly_entropy  42.6242     7.0907   6.011 3.69e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.077 on 472 degrees of freedom
Multiple R-squared:  0.3255,    Adjusted R-squared:  0.3198 
F-statistic: 56.94 on 4 and 472 DF,  p-value: < 2.2e-16

All 4 predictors that were eventually used for model building - MCQ_ind, EXE_ind, RES_ind, and weekly_entropy - proved highly significant.

R-squared: 0.3255 (adjusted R-squared: 0.3198)

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm11$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm11)
par(mfrow=c(1, 1))

# normality is fulfilled, but the homoscedasticity requirements is questionable
check.homoschedasticity(lm11)

# not good, there outliers and/or influential points
# check the outliers
lm11.data[c(49,294,50),]
# - 294 and 50: low to moderate activity indicators and zero final exam score
# - 230: moderate activity indicators, but excellent exam score
# check influential points
inf.indices <- as.numeric(names(head(sort(cooks.distance(lm11), decreasing = T))))
lm11.data[inf.indices,]
# observations with ordinal numbers  163, 241, 336 should be considered for removal
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:5)
  print(cor.test(lm11.data[,c], lm11$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm11)
## ontopic_ind and MCQ_ind have values > 2; remove ontopic_ind as it is not significant
# now (after ontopic_ind was removed), it's fine

If the model is to be used, the outliers should be dealt with.

Model 12: Extend model 10 with number of study sessions per weekday, and weekday entropy of study session counts

In addition to predictors from Model 10 and weekday entropy of study session counts, use, as predictors, study session counts for those week days that proved as significant predictors in Model 5 (Mon, Tue, Wed, Thu).

Loading the data…

weekday.sessions <- read.csv("Intermediate_results/regularity_of_study/weekday_session_props.csv")
lm12.data <- merge(x = lm10.data,
                   y = weekday.sessions %>% select(user_id, Mon_count, Tue_count, Wed_count,
                                                   Thu_count, weekday_entropy), 
                   by = "user_id", all.x = TRUE, all.y = FALSE)
# summary(lm12.data)
lm12.data <- lm12.data[,c(1:5,7:11,6)]
plot.correlations(lm12.data %>% select(-user_id))

lm12 <- lm(SC_FE_TOT ~ ., data = lm12.data %>% select(-c(user_id, ontopic_ind, RES_ind)))
summary(lm12)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm12.data %>% select(-c(user_id, 
    ontopic_ind, RES_ind)))

Residuals:
     Min       1Q   Median       3Q      Max 
-26.9153  -5.6781  -0.2458   5.9728  21.6389 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     22.93612    2.25082  10.190  < 2e-16 ***
MCQ_ind          0.59566    0.15805   3.769 0.000185 ***
EXE_ind         -1.00543    0.14347  -7.008 8.45e-12 ***
Mon_count        0.14117    0.04520   3.123 0.001901 ** 
Tue_count        0.11040    0.03598   3.068 0.002278 ** 
Wed_count        0.08320    0.04090   2.034 0.042474 *  
Thu_count        0.12923    0.04152   3.112 0.001970 ** 
weekday_entropy 26.94642    6.01675   4.479 9.45e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8.085 on 469 degrees of freedom
Multiple R-squared:  0.3285,    Adjusted R-squared:  0.3185 
F-statistic: 32.78 on 7 and 469 DF,  p-value: < 2.2e-16

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm12$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm12)
par(mfrow=c(1, 1))

# both normality and homoscedasticity requirements are fulfilled
# there are few outliers (230, 50, 459), but they do not look that significant
# check influential points
inf.indices <- as.numeric(names(head(sort(cooks.distance(lm12), decreasing = T))))
lm12.data[inf.indices,]
# observations with ordinal numbers  459, 22, 292 should be considered for removal
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:10)
  print(cor.test(lm12.data[,c], lm12$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm12)
## ontopic_ind has value > 2; remove it
## now RES_ind stands out with high value; remove it
# now (after ontopic_ind and RES_ind were removed), it's fine

Model 13: Extends model 12 with entropy of weekly study session counts

Loading the data…

weekly.sessions <- read.csv("Intermediate_results/regularity_of_study/weekly_session_props.csv")
lm13.data <- merge(x = lm12.data %>% select(-c(ontopic_ind, RES_ind)), 
                   y = weekly.sessions %>% select(user_id, weekly_entropy),
                   by = 'user_id', all.x = TRUE, all.y = FALSE)
#summary(lm13.data)
lm13.data <- lm13.data[,c(1:8,10,9)]
plot.correlations(lm13.data %>% select(-user_id))

lm13 <- lm(SC_FE_TOT ~ ., data = lm13.data %>% select(-user_id))
summary(lm13)

Call:
lm(formula = SC_FE_TOT ~ ., data = lm13.data %>% select(-user_id))

Residuals:
     Min       1Q   Median       3Q      Max 
-25.4716  -5.6469  -0.3401   5.5829  20.8140 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     25.49497    2.31501  11.013  < 2e-16 ***
MCQ_ind          0.64099    0.15619   4.104 4.79e-05 ***
EXE_ind         -1.05966    0.14208  -7.458 4.29e-13 ***
Mon_count        0.12474    0.04475   2.788 0.005526 ** 
Tue_count        0.08943    0.03587   2.493 0.013013 *  
Wed_count        0.07164    0.04041   1.773 0.076912 .  
Thu_count        0.10852    0.04127   2.630 0.008832 ** 
weekday_entropy 17.59079    6.40524   2.746 0.006259 ** 
weekly_entropy  29.83472    7.72548   3.862 0.000128 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 7.968 on 468 degrees of freedom
Multiple R-squared:  0.3492,    Adjusted R-squared:  0.3381 
F-statistic: 31.39 on 8 and 468 DF,  p-value: < 2.2e-16

R-squared: 0.349 (adjusted R-squared: 0.338)

Checking if the model satisfies the assumptions for linear regression:

# assumption 1: the mean of residuals is zero
mean(lm13$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm13)
par(mfrow=c(1, 1))

check.homoschedasticity(lm13)

# normality is fulfilled
# homoscedasticity is somewhat questionable - there are outliers and/or influential points
# outliers: 230, 50, 459
# check influential points
inf.indices <- head(sort(cooks.distance(lm13), decreasing = T))
inf.indices
lm13.data[as.numeric(names(inf.indices)),]
# observations with ordinal numbers  459, 163, 336, and 241 should be considered for removal (all have final exam score = zero)
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:10)
  print(cor.test(lm13.data[,c], lm13$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm13)
# it's fine
LS0tCnRpdGxlOiAiUHJlZGljdGl2ZSBtb2RlbHMgYmFzZWQgb24gdmFyaW91cyBpbmRpY2F0b3JzIG9mIHN0dWRlbnQgZW5nYWdlbWVudCBhbmQvb3IgcmVndWxhcml0eSBvZiBzdHVkeSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCgojIGxvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcyBhbmQgZnVuY3Rpb25zCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGNhcikKCiMgZm9yIGNvcnJlbGF0aW9uIHBsb3RzCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2JyaWF0dGUvZ2djb3JyL21hc3Rlci9nZ2NvcnIuUiIpCmBgYAoKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyMgc29tZSBhdXhpbGlhcnkgZnVuY3Rpb25zCgpwbG90LmNvcnJlbGF0aW9ucyA8LSBmdW5jdGlvbihkYXRhc2V0KSB7CiAgZ2djb3JyKGRhdGFzZXQsIG1ldGhvZCA9IGMoImNvbXBsZXRlIiwic3BlYXJtYW4iKSwgCiAgICAgICAjICAgICAgZ2VvbSA9ICJjaXJjbGUiLCBtaW5fc2l6ZSA9IDAsIG1heF9zaXplID0gMTUsCiAgICAgICBsYWJlbCA9IFRSVUUsIGxhYmVsX3NpemUgPSAzLjUsCiAgICAgICBoanVzdCA9IDAuODUsIHNpemUgPSA0LCBsYXlvdXQuZXhwID0gMSkKfQoKCiMjIHRoZSBmLiBzY2FsZXMgdGhlIGdpdmVuIGZlYXR1cmUgc2V0IGJ5IHN0YW5kYXJkaXppbmcgdGhlbQojIyBhcyBmZWF0dXJlcyBhcmUgZXhwZWN0ZWQgdG8gaGF2ZSBvdXRsaWVycywgaW5zdGVhZCBvZiB1c2luZyBtZWFuIGFuZCBTRCwgCiMjIG1lZGlhbiBhbmQgSW50ZXJxdWFydGlsZSBSYW5nZSAoSVFSKSBhcmUgdXNlZCwgYXMgc3VnZ2VzdGVkIGhlcmU6CiMjIGh0dHA6Ly9zY2lraXQtbGVhcm4ub3JnL3N0YWJsZS9tb2R1bGVzL3ByZXByb2Nlc3NpbmcuaHRtbCNzY2FsaW5nLWRhdGEtd2l0aC1vdXRsaWVycwpzY2FsZS5mZWF0dXJlcyA8LSBmdW5jdGlvbihmZWF0dXJlcykgewogIG0gPC0gbWF0cml4KG5yb3cgPSBucm93KGZlYXR1cmVzKSwgbmNvbCA9IG5jb2woZmVhdHVyZXMpLCBieXJvdyA9IEZBTFNFKQogIGkgPC0gMQogIGZvcihmIGluIGZlYXR1cmVzKSB7CiAgICBpZiAoIElRUih4ID0gZiwgbmEucm0gPSBUKSAhPSAwICkKICAgICAgbVssaV0gPC0gKGYgLSBtZWRpYW4oZiwgbmEucm0gPSBUKSkvSVFSKGYsIG5hLnJtID0gVCkKICAgIGVsc2UKICAgICAgbVssaV0gPC0gMAogICAgaSA8LSBpICsgMSAgCiAgfQogIHNjYWxlZC5kYXRhIDwtIGRhdGEuZnJhbWUobSkKICBjb2xuYW1lcyhzY2FsZWQuZGF0YSkgPC0gY29sbmFtZXMoZmVhdHVyZXMpCiAgc2NhbGVkLmRhdGEKfQoKCiMjIHRoZSBmLiBkcmF3cyBhIHBsb3QgdGhhdCBhbGxzIGZvciBjaGVja2luZyBpZiB0aGUgaG9tb3NjaGVkYXN0aWNpdHkgcmVxdWlyZW1lbnQgaXMKIyMgc2F0aXNmaWVkOyB3aGF0IHdlIHdhbnQgdG8gc2VlIG9uIHRoZSBwbG90IGlzIHRoYXQgdGhlIHNtb290aCBsaW5lIChyZXNpZHVhbCBkZXZpYXRpb24gZnJvbSB0aGUgYmVzdC1maXQgbGluZSkgbWF0Y2hlcyBhcyBtdWNoIGFzIHBvc3NpYmxlIHRoZSBkYXNoZWQgbGluZSAobm8gZGV2aWF0aW9uIGZyb20gdGhlIGJlc3QtZml0IGxpbmUpCmNoZWNrLmhvbW9zY2hlZGFzdGljaXR5IDwtIGZ1bmN0aW9uKGxtb2QpIHsKICBwbG90KGZpdHRlZChsbW9kKSwgcmVzaWQobG1vZCx0eXBlPSJwZWFyc29uIiksIGNvbD0iYmx1ZSIsIAogICAgIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsIHlsYWIgPSAiUmVzaWR1YWxzIikgCiAgYWJsaW5lKGg9MCxsd2Q9MikKICBsaW5lcyhzbW9vdGguc3BsaW5lKGZpdHRlZChsbW9kKSwgcmVzaWR1YWxzKGxtb2QpKSwgbHdkPTIsIGNvbD0ncmVkJykKfQpgYGAKCgojIE1vZGVscyBiYXNlZCBvbiAnYmFzaWMnIGluZGljYXRvcnMgb2YgZW5nYWdlbWVudCBhbmQgcmVndWxhcml0eSBvZiBzdHVkeSAKCkxvYWRpbmcgdGhlIHJlcXVpcmVkIGRhdGEKYGBge3IgcmVzdWx0cz0naGlkZSd9CmRhaWx5LmNvdW50cyA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS93ZWVrbHlfY291bnRzX29mX2RhaWx5X2xvZ2luc193Mi0xMy5jc3YiKQojY29sbmFtZXMoZGFpbHkuY291bnRzKQoKd2Vla2x5LmNvdW50cyA8LSBkYWlseS5jb3VudHMgJT4lCiAgc2VsZWN0KHVzZXJfaWQsIFcyX2NudDpXMTNfY250LCB0b3RfY250LCB3ZWVrbHlfZW50cm9weSkKIyBzdHIod2Vla2x5LmNvdW50cykKCmRhaWx5LmdhcHMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvZ2Fwc19iZXR3ZWVuX2NvbnNlY3V0aXZlX2xvZ2luc193Mi0xMy5jc3YiKQojIHN0cihkYWlseS5nYXBzKQojIGRhaWx5IGdhcHMgZG8gbm90IGhhdmUgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgc28sIG1lZGlhbiB3aWxsIGJlIHVzZWQKCiMgbWVyZ2Ugd2Vla2x5IGNvdW50cyBhbmQgbWVkaWFuIHRpbWUgZ2FwIApjb3VudHMuZGF0YSA8LSBtZXJnZSh4ID0gd2Vla2x5LmNvdW50cywgeSA9IGRhaWx5LmdhcHMgJT4lIHNlbGVjdCh1c2VyX2lkLCBtZWRpYW5fZ2FwKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSAndXNlcl9pZCcsIGFsbCA9IFRSVUUpCgpleGFtLnNjb3JlcyA8LSByZWFkLmNzdihmaWxlID0gIkludGVybWVkaWF0ZV9yZXN1bHRzL2V4YW1fc2NvcmVzX3dpdGhfc3R1ZGVudF9pZHMuY3N2IikKIyByZW1vdmUgZW1haWwgZGF0YQpleGFtLnNjb3JlcyA8LSBleGFtLnNjb3JlcyAlPiUgc2VsZWN0KC0yKQojIHN0cihleGFtLnNjb3JlcykKCiMgbWVyZ2UgY291bnRzIGRhdGEgd2l0aCBleGFtIHNjb3Jlcwpjb3VudHMuZGF0YSA8LSBtZXJnZSh4ID0gY291bnRzLmRhdGEsIHkgPSBleGFtLnNjb3JlcywgYnkueCA9ICd1c2VyX2lkJywgYnkueSA9ICdVU0VSX0lEJywgCiAgICAgICAgICAgICAgICAgICAgIGFsbC54ID0gVCwgYWxsLnkgPSBGKQoKI3N1bW1hcnkoY291bnRzLmRhdGEpCiMgOSBOQSB2YWx1ZXMgZm9yIGV4YW0gc2NvcmVzOyByZW1vdmUgdGhlbQpjb3VudHMuZGF0YSA8LSBjb3VudHMuZGF0YSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCmBgYAoKIyMjIE1vZGVsIDE6IHByZWRpY3RvcnMgYXJlIHRoZSBzYW1lIGFzIHRob3NlIHVzZWQgaW4gdGhlIGxhdGVzdCBjbHVzdGVyIG1vZGVsCgpUaGlzIG1lYW5zIHRoYXQgcHJlZGljdG9ycyBhcmUgY291bnRzIG9mIGFjdGl2ZSBkYXlzIChkYXlzIHdoZW4gYSBzdHVkZW50IGhhZCBhdCBsZWFzdCBvbmUgbGVhcm5pbmcgc2Vzc2lvbikgcGVyIHdlZWssIGVudHJvcHkgb2Ygd2Vla2x5IGFjdGl2ZSBkYXlzLCBhbmQgbWVkaWFuIGdhcCBiZXR3ZWVuIHR3byBjb25zZWN1dGl2ZSBhY3RpdmUgZGF5cy4gCgpgYGB7cn0KbG0xLmRhdGEgPC0gY291bnRzLmRhdGEgJT4lIHNlbGVjdCgtYyh0b3RfY250LCB1c2VyX2lkLCBTQ19NVF9UT1QpKQpsbTEgPC0gbG0oU0NfRkVfVE9UIH4gLiwgZGF0YSA9IGxtMS5kYXRhKQpzdW1tYXJ5KGxtMSkKYGBgCkl0J3MgaW50ZXJlc3RpbmcgdGhhdCBjb3VudHMgZm9yIG9ubHkgMyB3ZWVrcyBhcmUgc2lnbmlmaWNhbnQgYW5kIHRoYXQgYWxsIHRocmVlIHdlZWtzIGFyZSBpbiB0aGUgMm5kIHBhcnQgb2YgdGhlIGNvdXJzZSAoYWZ0ZXIgbWlkdGVybSBleGFtKTogCgoqIG9uZSBkYXkgbW9yZSBvZiBzdHVkeSBpbiB3ZWVrIDggY29udHJpYnV0ZXMgMS4wOSBwb2ludHMgdG8gdGhlIGZpbmFsIGV4YW0gc2NvcmU7IAoqIG9uZSBkYXkgbW9yZSBpbiB3ZWVrIDEwIGNvbnRyb2J1dGVzIDEuMDggcG9pbnRzLCBhbmQgCiogb25lIGFjdGl2ZSBkYXkgbW9yZSBpbiB3ZWVrIDEzIGFkZHMgMC44NiBwb2ludHMuCgpSLXNxdWFyZWQgaXMgMC4yODMJKGFkanVzdGVkIFIyOiAwLjI2MSkuCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTEkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG0xKQpwYXIobWZyb3c9YygxLDEpKSAjIENoYW5nZSBiYWNrIHRvIDEgeCAxCiMgdGhlcmUgYXJlIGZldyBwb3RlbnRpYWwgaW5mbHVlbnRpYWwgcG9pbnRzOiA4MCwgNTAsIDQ1OQoKIyMgYXNzdW1wdGlvbiA0OiBwcmVkaWN0b3JzIGFuZCByZXNpZHVhbHMgYXJlIHVuY29ycmVsYXRlZApmb3IoYyBpbiAxOjE0KQogIHByaW50KGNvci50ZXN0KGxtMS5kYXRhWyxjXSwgbG0xJHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG0xKQojIE9LLCB2YWx1ZXMgYmVsb3cgb3IgZXF1YWwgdG8gMgpgYGAKVGhlIGFzc3VtcHRpb25zIGFyZSBzYXRpc2lmZWQsIHRob3VnaCB0aGVyZSBhcmUgZmV3IHBvdGVudGlhbGx5IGluZmx1ZW50aWFsIHBvaW50cyB0aGF0IG1pZ2h0IG5lZWQgdG8gYmUgY29uc2lkZXJlZCBpZiB0aGlzIG1vZGVsIGlzIHRvIGJlIHVzZWQgCgoKCiMjIyBNb2RlbCAyOiBMaWtlIE1vZGVsIDEsIGJ1dCBpbnN0ZWFkIG9mIHdlZWtseSBjb3VudHMsIHVzZXMgdG90YWwgbnVtYmVyIG9mIGFjdGl2ZSBkYXlzIGR1cmluZyB0aGUgY291cnNlCgpgYGB7cn0KbG0yLmRhdGEgPC0gY291bnRzLmRhdGEgJT4lIHNlbGVjdCh0b3RfY250LCBtZWRpYW5fZ2FwLCB3ZWVrbHlfZW50cm9weSwgU0NfRkVfVE9UKQpsbTIgPC0gbG0oU0NfRkVfVE9UIH4gLiwgZGF0YSA9IGxtMi5kYXRhKQpzdW1tYXJ5KGxtMikKYGBgCgpUaGUgdG90YWwgbnVtYmVyIG9mIGFjdGl2ZSBkYXlzIGlzIHRoZSBvbmx5IHNpZ25pZmljYW50IHByZWRpY3RvciwgYW5kIGl0IGlzIGhpZ2hseSBzaWduaWZpY2FudC4gRWFjaCBhZGRpdGlvbmFsIGFjdGl2ZSBkYXkgY29udHJpYnV0ZXMgMC40IHBvaW50cyB0byB0aGUgZmluYWwgZXhhbSBzY29yZS4KClItc3F1YXJlZCBpcyAgMC4yNTU3CShhZGp1c3RlZCBSMjogMC4yNTEpLiAKCkNoZWNraW5nIGlmIHRoZSBtb2RlbCBzYXRpc2ZpZXMgdGhlIGFzc3VtcHRpb25zIGZvciBsaW5lYXIgcmVncmVzc2lvbjoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgYXNzdW1wdGlvbiAxOiB0aGUgbWVhbiBvZiByZXNpZHVhbHMgaXMgemVybwptZWFuKGxtMiRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTIpCnBhcihtZnJvdz1jKDEsMSkpICMgQ2hhbmdlIGJhY2sgdG8gMSB4IDEKIyBib3RoIE9LCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6MykKICBwcmludChjb3IudGVzdChsbTIuZGF0YVssY10sIGxtMiRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtMikKIyBPSywgdmFsdWVzIGJlbG93IG9yIGVxdWFsIHRvIDIKYGBgCkFsbCBhc3N1bXB0aW9ucyBhcmUgc2F0aXNpZmllZC4KCgojIyMgTW9kZWwgMzogTnVtYmVyIG9mIHN0dWR5IHNlc3Npb25zIHBlciB3ZWVrLCB3ZWVrbHkgZW50cm9weSBvZiBzdHVkeSBzZXNzaW9uIGNvdW50cywgYW5kIHRpbWUgZ2FwIGJldHdlZW4gY29uc2VjdXRpdmUgc2Vzc2lvbnMKCkxvYWRpbmcgdGhlIHJlcXVpcmVkIGRhdGEKYGBge3IgcmVzdWx0cz0naGlkZSd9CndlZWtseS5zZXNzaW9ucyA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS93ZWVrbHlfc2Vzc2lvbl9wcm9wcy5jc3YiKQojc3RyKHdlZWtseS5zZXNzaW9ucykKCnNlcy5nYXAuZGF0YSA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS9pbnRlci1zZXNzaW9uX3RpbWVfaW50ZXJ2YWxzLmNzdiIpICNzdHIoc2VzLmdhcC5kYXRhKQoKbG0zLmRhdGEgPC0gbWVyZ2UoeCA9IHdlZWtseS5zZXNzaW9ucyAlPiUgc2VsZWN0KGNvdW50X3cyOmNvdW50X3cxMiwgd2Vla2x5X2VudHJvcHksIHVzZXJfaWQpLAogICAgICAgICAgICAgICAgICB5ID0gc2VzLmdhcC5kYXRhICU+JSBzZWxlY3QodXNlcl9pZCwgbWVkaWFuX3NfZ2FwKSwKICAgICAgICAgICAgICAgICAgYnkgPSAndXNlcl9pZCcsIGFsbCA9IFRSVUUpCmxtMy5kYXRhIDwtIG1lcmdlKHggPSBsbTMuZGF0YSwgeSA9IGV4YW0uc2NvcmVzICU+JSBzZWxlY3QoVVNFUl9JRCwgU0NfRkVfVE9UKSwKICAgICAgICAgICAgICAgICAgYnkueCA9ICd1c2VyX2lkJywgYnkueSA9ICdVU0VSX0lEJywgYWxsLnggPSBULCBhbGwueSA9IEYpCnN1bW1hcnkobG0zLmRhdGEpCgojIyByZW1vdmUgcm93cyB3aXRoIE5BcwpsbTMuZGF0YSA8LSBsbTMuZGF0YSAlPiUgZmlsdGVyKGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICYgaXMubmEobWVkaWFuX3NfZ2FwKT09RkFMU0UpIAoKIyAjIyBzaW5jZSBzb21lIG9mIHRoZSBwcmVkaWN0b3JzIGFyZSBvbiBxdWl0ZSBkaWZmZXJlbnQgc2NhbGVzLCByZXNjYWxlIHRoZW0KIyBhcHBseShsbTMuZGF0YSAlPiUgc2VsZWN0KC11c2VyX2lkKSwgMiwgc2hhcGlyby50ZXN0KQojIGFwcGx5KGxtMy5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpLCAyLCBmdW5jdGlvbih4KSBsZW5ndGgoYm94cGxvdC5zdGF0cyh4KSRvdXQpKQojICMgYWxsIHByZWRpdG9ycyBoYXZlIG91dGxpZXJzIC0+IG5vcm1hbGl6YXRpb24gaXMgbm90IGFkdmlzZWQKIyBsbTMuc2MuZGF0YSA8LSBzY2FsZS5mZWF0dXJlcyhsbTMuZGF0YSAlPiUgc2VsZWN0KC1jKHVzZXJfaWQsIFNDX0ZFX1RPVCkpKQojIGxtMy5zYy5kYXRhIDwtIGNiaW5kKGxtMy5zYy5kYXRhLCBTQ19GRV9UT1Q9bG0zLmRhdGEkU0NfRkVfVE9UKQojIHN1bW1hcnkobG0zLnNjLmRhdGEpCgojIyB0aGUgc2FtZSByZXN1bHRzIGFyZSBvYnRhaW5lZCB3aXRoIHNjYWxlZCBhbmQgdW5zY2FsZWQgKG9yaWdpbmFsKSBkYXRhOwojIyB3aWxsIGtlZXAgdGhlIHJlc3VsdHMgd2l0aCBvcmlnaW5hbCBkYXRhIGFzIHRoZXkgYXJlIGVhc2llciB0byBpbnRlcnByZXQKCmxtMy5kYXRhIDwtIGxtMy5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpCmBgYAoKYGBge3J9CmxtMyA8LSBsbShTQ19GRV9UT1QgfiAuLCBkYXRhID0gbG0zLmRhdGEpCnN1bW1hcnkobG0zKQpgYGAKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIHNlc3Npb24gY291bnRzIGluIHdlZWsgNTogbmVnYXRpdmUgaW1wYWN0ICghKSwgYW4gYWRkaXRpb25hbCBzZXNzaW9uIGRlY3JlYXNlcyBleGFtIHNjb3JlIGJ5IDAuMjgKKiBzZXNzaW9uIGNvdW50cyBpbiB3ZWVrIDEwOiBhbiBhZGRpdGlvbmFsIHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC41MgoqIHNlc3Npb24gY291bnRzIGluIHdlZWsgMTE6IGFuIGFkZGl0aW9uYWwgc2Vzc2lvbiBpbmNyZWFzZXMgZXhhbSBzY29yZSBieSAwLjM3Ciogd2Vla2x5IGVudHJvcHk6IGlmIGVudHJvcHkgaW5jcmVhc2VzIGJ5IDEsIGV4YW0gc2NvcmUgaW5jcmVhc2VzIGJ5IDI1OyBob3dldmVyLCBlbnRyb3B5IGNhbm5vdCBpbmNyZWFzZSBub3QgbmVhcmx5IHRoYXQgbXVjaDsgdGhpcyBzaW1wbHkgY29uZmlybXMgdGhhdCB0aGUgaGlnaGVyIHRoZSByZWd1bGFyaXR5IChoaWdoIGVudHJvcHkgbWVhbnMgdGhhdCB3ZWVrbHkgY291bnRzIGFyZSBhbG1vc3QgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkKSwgdGhlIGhpZ2hlciB0aGUgcGVyZm9ybWFuY2UKClItc3F1YXJlZDogMC4yOTQgKGFkanVzdGVkIFIyOiAwLjI3NSkuCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTMkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG0zKQpwYXIobWZyb3c9YygxLDEpKSAjIENoYW5nZSBiYWNrIHRvIDEgeCAxCiMgbW9zdGx5IGZpbmUsIGJ1dCB0aGVyZSBhcmUgZmV3IChwb3RlbnRpYWxseSkgaW5mbHVlbnRpYWwgcG9pbnRzOiA0MTIsIDQ1OSwgNDM3LCA3NwojIGxldCdzIGV4YW1pbmUgdGhlbQpsbTMuZGF0YVtjKDQxMiw0NTksNDM3LDc3KSxdCnN1bW1hcnkobG0zLmRhdGEpCiMgNDM3IGhhcyB2ZXJ5IGxvdyBlbmdhZ2VtZW50IGFuZCB2ZXJ5IGhpZ2ggZXhhbSBzY29yZSAoMzUpCiMgNDU5IGhhcyBoaWdoIGVuZ2FnZW1lbnQgKGF0IHRpbWVzIHZlcnkgaGlnaCkgYW5kIHplcm8gKDApIGV4YW0gc2NvcmUKIyA0MTIgaXMgc2ltaWxhciB0byA0NTksIGJ1dCBub3QgdGhhdCBleHRyZW1lICg3IGV4YW0gc2NvcmU7IGxlc3MgYWN0aXZlKQojIDc3IGlzIGFsbW9zdCBjb21wbGV0ZWx5IGluYWN0aXZlLCBhbmQgaGFzIHplcm8gKDApIGV4YW0gc2NvcmUKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKbG0zLmRhdGEgPC0gbG0zLmRhdGEgJT4lIGZpbHRlcihpcy5uYShtZWRpYW5fc19nYXApPT1GQUxTRSkKZm9yKGMgaW4gMToxMikKICBwcmludChjb3IudGVzdChsbTMuZGF0YVssY10sIGxtMyRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtMykKIyBPSywgdmFsdWVzIGJlbG93IG9yIHNsaWdodGx5IGFib3ZlIDIKYGBgCgoKIyMjIE1vZGVsIDQ6IFRvdGFsIG51bWJlciBvZiBzdHVkeSBzZXNzaW9ucywgd2Vla2x5IGVudHJvcHkgb2Ygc3R1ZHkgc2Vzc2lvbiBjb3VudHMsIGFuZCB0aW1lIGdhcCBiZXR3ZWVuIGNvbnNlY3V0aXZlIHNlc3Npb25zCgpgYGB7cn0KbG00LmRhdGEgPC0gbWVyZ2UoeCA9IHdlZWtseS5zZXNzaW9ucyAlPiUgc2VsZWN0KHVzZXJfaWQsIHNfdG90YWwsIHdlZWtseV9lbnRyb3B5KSwKICAgICAgICAgICAgICAgICAgeSA9IHNlcy5nYXAuZGF0YSAlPiUgc2VsZWN0KC1tYWRfc19nYXApLAogICAgICAgICAgICAgICAgICBieSA9ICd1c2VyX2lkJywgYWxsID0gVFJVRSkKbG00LmRhdGEgPC0gbWVyZ2UoeCA9IGxtNC5kYXRhLCB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdChVU0VSX0lELCBTQ19GRV9UT1QpLAogICAgICAgICAgICAgICAgICBieS54ID0gJ3VzZXJfaWQnLCBieS55ID0gJ1VTRVJfSUQnLCBhbGwueCA9IFQsIGFsbC55ID0gRikKCmxtNC5kYXRhIDwtIGxtNC5kYXRhICU+JSBmaWx0ZXIoIGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICkgJT4lIHNlbGVjdCgtdXNlcl9pZCkKYGBgCgpgYGB7cn0KbG00IDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTQuZGF0YSkKc3VtbWFyeShsbTQpCmBgYAoKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIHRvdGFsIG51bWJlciBvZiBzZXNzaW9uczogYW4gYWRkaXRpb25hbCBzZXNzaW9uIGluY3JlYXNlcyBleGFtIHNjb3JlIGJ5IDAuMTIKKiB3ZWVrbHkgZW50cm9weTogaWYgZW50cm9weSBpbmNyZWFzZXMgYnkgMSwgZXhhbSBzY29yZSBpbmNyZWFzZXMgYnkgMzEuODg7IGhvd2V2ZXIsIGVudHJvcHkgY2Fubm90IGluY3JlYXNlIG5vdCBuZWFybHkgdGhhdCBtdWNoOyB0aGlzIHNpbXBseSBjb25maXJtcyB0aGF0IHRoZSBoaWdoZXIgdGhlIHJlZ3VsYXJpdHkgKGhpZ2ggZW50cm9weSBtZWFucyB0aGF0IHdlZWtseSBjb3VudHMgYXJlIGFsbW9zdCB1bmlmb3JtbHkgZGlzdHJpYnV0ZWQpLCB0aGUgaGlnaGVyIHRoZSBwZXJmb3JtYW5jZQoKUi1zcXVhcmVkOiAwLjIyOSAoYWRqdXN0ZWQgUjI6IDAuMjI0KS4KCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTQkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG00KQpwYXIobWZyb3c9YygxLCAxKSkKIyB0aGUgUmVzaWR1YWxzIHZzIEZpdHRlZCBwbG90IHN1Z2dlc3RzIHRoYXQgdGhlcmUgbWlnaHQgYmUgc29tZSBub24tbGluZWFyIHJlYWx0aW9uc2hpcCBiZXR3ZWVuIHRoZSBvdXRjb21lIGFuZCB0aGUgcHJlZGljdG9ycwojIHRoZXJlIGFyZSBhbHNvIGZldyBpbmZsdWVudGlhbCBwb2ludHM6IDQxMiwgNDU5LCAzNzYsIDc3CiMgbGV0J3MgZXhhbWluZSB0aGVtCmxtNC5kYXRhW2MoNDEyLDQ1OSwzNzYsIDc3KSxdCnN1bW1hcnkobG00LmRhdGEpCiMgNzcgaXMgYSBjbGVhciBvdXRsaWVyCiMgMzc2IGhhcyByZWxhdGl2ZWx5IGhpZ2ggZW5nYWdlbWVudCAoYWJvdmUgM3JkIHF1YXJ0aWxlKSwgYnV0IHZlcnkgbG93IGV4YW0gc2NvcmUgKDQpCiMgNDEyIGFuZCA0NTkgaGF2ZSBhbHJlYWR5IGJlZW4gZXhhbWluZWQgYmVmb3JlCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmxtNC5kYXRhIDwtIGxtNC5kYXRhICU+JSBmaWx0ZXIoaXMubmEobWVkaWFuX3NfZ2FwKT09RkFMU0UpCmZvcihjIGluIDE6MykKICBwcmludChjb3IudGVzdChsbTQuZGF0YVssY10sIGxtNCRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtNCkKIyBPSywgdmFsdWVzIGJlbG93IDIKYGBgCgoKIyMjIE1vZGVsIDU6IE51bWJlciBvZiBzdHVkeSBzZXNzaW9ucyBwZXIgd2VlayBkYXksIGFuZCB3ZWVrIGRheSBlbnRyb3B5IG9mIHN0dWR5IHNlc3Npb24gY291bnRzCgpMb2FkaW5nIHRoZSBkYXRhCmBgYHtyfQp3ZWVrZGF5LnNlc3Npb25zIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3dlZWtkYXlfc2Vzc2lvbl9wcm9wcy5jc3YiKQojc3RyKHdlZWtkYXkuc2Vzc2lvbnMpCgpsbTUuZGF0YSA8LSBtZXJnZSh4ID0gd2Vla2RheS5zZXNzaW9ucyAlPiUgc2VsZWN0KDE6OCwgMTEpLAogICAgICAgICAgICAgICAgICB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdCgtU0NfTVRfVE9UKSwKICAgICAgICAgICAgICAgICAgYnkueCA9ICJ1c2VyX2lkIiwgYnkueSA9ICJVU0VSX0lEIiwKICAgICAgICAgICAgICAgICAgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFKQojIHN1bW1hcnkobG01LmRhdGEpCmxtNS5kYXRhIDwtIGxtNS5kYXRhICU+JSBmaWx0ZXIoIGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICkgJT4lIHNlbGVjdCgtdXNlcl9pZCkKYGBgCgpgYGB7cn0KbG01IDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTUuZGF0YSkKc3VtbWFyeShsbTUpCmBgYAoKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIE1vbmRheSBzZXNzaW9uIGNvdW50czogYW4gYWRkaXRpb25hbCBNb24gc2Vzc2lvbiBpbmNyZWFzZXMgZXhhbSBzY29yZSBieSAwLjIwNAoqIFR1ZXNkYXkgc2Vzc2lvbiBjb3VudHM6IGFuIGFkZGl0aW9uYWwgVHVlIHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC4xMTcKKiBXZWRuZXNkYXkgc2Vzc2lvbiBjb3VudHM6IGFuIGFkZGl0aW9uYWwgV2VkIHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC4xCiogVGh1cnNkYXkgc2Vzc2lvbiBjb3VudHM6IGFuIGFkZGl0aW9uYWwgVGh1IHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC4xNjEKKiB3ZWVrbHkgZW50cm9weTogaWYgZW50cm9weSBpbmNyZWFzZXMgYnkgMSwgZXhhbSBzY29yZSBpbmNyZWFzZXMgYnkgMTcuNjsgaG93ZXZlciwgZW50cm9weSBjYW5ub3QgaW5jcmVhc2Ugbm90IG5lYXJseSB0aGF0IG11Y2g7IHRoaXMgc2ltcGx5IGNvbmZpcm1zIHRoYXQgdGhlIGhpZ2hlciB0aGUgcmVndWxhcml0eSAoaGlnaCBlbnRyb3B5IG1lYW5zIHRoYXQgd2Vla2x5IGNvdW50cyBhcmUgYWxtb3N0IHVuaWZvcm1seSBkaXN0cmlidXRlZCksIHRoZSBoaWdoZXIgdGhlIHBlcmZvcm1hbmNlCgpSLXNxdWFyZWQ6IDAuMjMxIChhZGp1c3RlZCBSMjogMC4yMTgpLgoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG01JHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtNSkKcGFyKG1mcm93PWMoMSwxKSkgIyBDaGFuZ2UgYmFjayB0byAxIHggMQojIG1vc3RseSBmaW5lLCBidXQgdGhlcmUgYXJlIGZldyBwb3RlbnRpYWxseSBpbmZsdWVudGlhbCBwb2ludHM6IHVzdWFsIHN1c3BlY3RzICg0MTIsIDQ1OSksIDIzMAojIGxldCdzIGV4YW1pbmUgdGhlbQpsbTUuZGF0YVtjKDQxMiw0NTksMjMwKSxdCnN1bW1hcnkobG01LmRhdGEpCiMgMjMwIGhhcyBsb3cgZW5nYWdlbWVudCBhbmQgdmVyeSBoaWdoIGV4YW0gc2NvcmUgKDM1KQojIDQ1OSBhbmQgNDEyIGhhdmUgYWxyZWFkeSBiZWVuIGNvbnNpZGVyZWQKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMTo4KQogIHByaW50KGNvci50ZXN0KGxtNS5kYXRhWyxjXSwgbG01JHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG01KQojIE9LLCB2YWx1ZXMgYmVsb3cgMgpgYGAKCgojIE1vZGVscyBiYXNlZCBvbiAnYWR2YW5jZWQnIGluZGljYXRvcnMgb2YgZW5nYWdlbWVudCBhbmQgcmVndWxhcml0eSBvZiBzdHVkeSAKCiMjIyBNb2RlbCA2OiBEYWlseSByZXNvdXJjZSB1c2UKCkFzIHByZWRpY3RvcnMsIHVzZSB0b3RhbCBjb3VudHMgb2YgZGlmZmVyZW50IGtpbmRzIG9mIHJlc291cmNlcyBzdHVkZW50cyB1c2VkIGR1cmluZyB0aGVpciBhY3RpdmUgZGF5cyAoYW4gYWN0aXZlIGRheSBpcyBhIGRheSB3aGVuIGEgc3R1ZGVudCBoYWQgYXQgbGVhc3Qgb25lIHN0dWR5IHNlc3Npb24pLiBUaGUgdHlwZXMgb2YgcmVzb3VyY2VzIGNvbnNpZGVyZWQ6IAoKKiB2aWRlbyAoVklERU8pCiogZXhlcmNpc2VzIChFWEUpCiogbXVsdGlwbGUgY2hvaWNlIHF1ZXN0aW9ucyAoTUNRKQoqIHJlYWRpbmcgbWF0ZXJpYWxzIChSRVMpCiogbWV0YWNvZ25pdGl2ZSBpdGVtcyAoTUVUQUNPRykKCkluIGFkZGl0aW9uLCBjb25zaWRlciB1c2luZzoKCiogbWVkaWFuX1hfY250IC0gbWVkaWFuIG51bWJlciBvZiByZXNvdXJjZXMgb2YgdHlwZSBYIHVzZWQgZHVyaW5nIHRoZSBzdHVkZW50J3MgYWN0aXZlIGRheXMgKFggY2FuIGJlIFZJREVPLCBFWEUsIE1DUSwgUkVTLCBNRVRBQ09HKQoqIG1hZF9YX2NudCAtIE1BRCAobWVkaWFuIGFic29sdXRlIGRldmlhdGlvbikgb2YgcmVzb3VyY2VzIG9mIHRoZSB0eXBlIFggdXNlZCBkdXJpbmcgdGhlIHN0dWRlbnQncyBhY3RpdmUgZGF5cwoqIGRheXNfWF91c2VkIC0gbnVtYmVyIG9mIGRheXMgd2hlbiByZXNvdXJjZXMgb2YgdGhlIHR5cGUgWCB3ZXJlIHVzZWQKKiBwcm9wX1hfdXNlZCAtIHByb3BvcnRpb24gb2YgZGF5cyB3aGVuIHJlc291cmNlcyBvZiB0aGUgdHlwZSBYIHdlcmUgdXNlZCB2ZXJzdXMgdG90YWwgbnVtYmVyIG9mIHRoZSBzdHVkZW50J3MgYWN0aXZlIGRheXMKCkxvYWRpbmcgdGhlIGRhdGEuLi4KYGBge3J9CnJlcy51c2Uuc3RhdHMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvZGFpbHlfcmVzb3VyY2VfdXNlX3N0YXRpc3RpY3NfdzItNV83LTEyLmNzdiIpCiNzdHIocmVzLnVzZS5zdGF0cykKCmxtNi5kYXRhIDwtIG1lcmdlKHJlcy51c2Uuc3RhdHMsIGV4YW0uc2NvcmVzLCBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFQsIGFsbC55ID0gRikKbG02LmRhdGEgPC0gbG02LmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBTQ19NVF9UT1QpKSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCmBgYAoKCiMjIyMgRmlyc3QsIGluY2x1ZGUgb25seSB0aGUgaW5kaWNhdG9ycyBvZiBlbmdhZ2VtZW50IChub3QgcmVndWxhcml0eSkKYGBge3IgcmVzdWx0cz0naGlkZSd9CmxtNl8xLmRhdGEgPC0gbG02LmRhdGEgJT4lIHNlbGVjdCggc3RhcnRzX3dpdGgoInRvdCIpLCBzdGFydHNfd2l0aCgicHJvcCIpLCBTQ19GRV9UT1QpCgojIGV4YW1pbmUgdGhlIHByZXNlbmNlIG9mIChoaWdoKSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMKZ2djb3JyKGxtNl8xLmRhdGEsIG1ldGhvZCA9IGMoImNvbXBsZXRlIiwic3BlYXJtYW4iKSwgCiAgICAgICAjICAgICAgZ2VvbSA9ICJjaXJjbGUiLCBtaW5fc2l6ZSA9IDAsIG1heF9zaXplID0gMTUsCiAgICAgICBsYWJlbCA9IFRSVUUsIGxhYmVsX3NpemUgPSAzLjUsCiAgICAgICBoanVzdCA9IDAuODUsIHNpemUgPSA0LCBsYXlvdXQuZXhwID0gMSkKCiMgdG90X21jb2dfY250IGFuZCBwcm9wX21jb2dfdXNlZCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIGFzIGFyZSB0b3RfdmlkZW9fY250IGFuZCBwcm9wX3ZpZGVvX3VzZWQsIGFuZCB0b3RfbWNxX2NudCBhbmQgcHJvcF9tY3FfdXNlZCAKbG02XzEuZGF0YSA8LSBsbTZfMS5kYXRhICU+JSBzZWxlY3QoLWMocHJvcF9tY29nX3VzZWQsIHByb3BfdmlkZW9fdXNlZCwgcHJvcF9tY3FfdXNlZCkpCmBgYAoKYGBge3J9CiMgcmVtb3ZlIHRoZSBvdXRsaWVycyBhbmQgcmUtcnVuIHRoZSBtb2RlbApsbTZfMS5kYXRhIDwtIGxtNl8xLmRhdGFbLWMoODYsIDQxMiwgNDYyLCA0NTkpLF0KbG02XzEgPC0gbG0oU0NfRkVfVE9UIH4uLCBkYXRhID0gbG02XzEuZGF0YSkKc3VtbWFyeShsbTZfMSkKYGBgClNpZ25pZmljYW50IHByZWRpY3RvcnM6IAoKKiB0b3RhbCBleGVyY2lzZSBjb3VudHMgKG51bWJlciBvZiBleGVyY2lzZS1yZWxhdGVkIGV2ZW50cyBkdXJpbmcgdGhlIHN0dWRlbnQncyBhY3RpdmUgZGF5cyk6IGFuIGFkZGl0aW9uYWwgZXhlcmNpc2UtcmVsYXRlZCBldmVudCAqZGVjcmVhc2VzKiB0aGUgZmluYWwgZXhhbSBzY29yZSBieSAwLjAwNjYKKiB0b3RhbCBNQ1EgY291bnRzIChudW1iZXIgb2YgTUNRLXJlbGF0ZWQgZXZlbnRzIGR1cmluZyB0aGUgc3R1ZGVudCdzIGFjdGl2ZSBkYXlzKTogYW4gYWRkaXRpb25hbCBNQ1EtcmVsYXRlZCBldmVudCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC4wMDc4CiogdG90YWwgbnVtYmVyIG9mIHJlYWRpbmcgcmVsYXRlZCBldmVudHM6IGFuIGFkZGl0aW9uYWwgcmVhZGluZy1yZWxhdGVkIGV2ZW50ICpkZWNyZWFzZXMqIHRoZSBmaW5hbCBleGFtIHNjb3JlIGJ5IDAuMDA5NgoKUi1zcXVhcmVkOiAwLjE5MiAoYWRqdXN0ZWQgUjI6IDAuMTgwKS4KCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTZfMSRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTZfMSkKcGFyKG1mcm93PWMoMSwgMSkpCiMgdW5jbGVhciBpZiBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50IGlzIGZ1bGZpbGxlZDsgY2hlY2sgdXNpbmcgdGhpcyBwbG90OgpjaGVjay5ob21vc2NoZWRhc3RpY2l0eShsbTZfMSkKIyBub3QgdGhhdCBnb29kCgojICMgdGhlIHBsb3RzIHBvaW50IHRvIGNvdXBsZSBvZiBvdXRsaWVyczogODYsIDQxMiwgNDYyLCA0NTkgCiMgIyBsZXQncyBjaGVjayB0aGVtOgojIGxtNl8xLmRhdGFbYyg4NiwgNDEyLCA0NjIsIDQ1OSksXQojIHN1bW1hcnkobG02XzEuZGF0YSkKIyAjIDQ1OSBhbmQgNDYyIGhhdmUgemVybyBleGFtIHNjb3JlLCBpbnNwaXRlIG9mIG5vbi1uZWdsaWdpYmxlIG51bWJlciBvZiBsZWFybmluZyBldmVudHMgKGVzcGVjaWFsbHkgNDU5KQojICMgNDEyIHdhcyBoaWdobHkgYWN0aXZlLCBidXQgaGFkIHZlcnkgbG93IGV4YW0gc2NvcmUgKDcpCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6NykKICBwcmludChjb3IudGVzdChsbTZfMS5kYXRhWyxjXSwgbG02XzEkcmVzaWR1YWxzKSkKIyBPSwoKIyMgYXNzdW1wdGlvbiA2OiBubyBtdWx0aWNvbGluZWFyaXR5IGJldHdlZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzCnZpZihsbTZfMSkKIyBpdCdzIGZpbmU6IGFsbCBiZWxvdyBvciBlcXVhbCB0byAyCmBgYApUaGUgYXNzdW1wdGlvbiBvZiBob21vc2NlZGFzdGljaXR5IGNhbm5vdCBiZSBjb25zaWRlcmVkIHNhdGlzZmllZCAoZXZlbiBhZnRlciByZW1vdmluZyBvdXRsaWVycykKCgojIyMjIE5vdywgaW5jbHVkZSBib3RoIGluZGljYXRvcnMgb2YgZW5nYWdlbWVudCBhbmQgaW5kaWNhdG9yIG9mIHJlZ3VsYXJpdHkKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGluY2x1ZGUgdGhvc2UgZW5nYWdtZW50IGluZGljYXRvcnMgdGhhdCBwcm92ZWQgYXQgbGVhc3Qgc2xpZ2h0bHkgcmVsZXZhbnQgaW4gdGhlIHByZXZpb3VzIG1vZGVsCiMgcGx1cyBtYWRfWF9jbnQgYXMgaW5kaWNhdG9ycyBvZiByZWd1bGFyaXR5CmxtNl8yLmRhdGEgPC0gbG02LmRhdGEgJT4lIHNlbGVjdCh0b3RfbWNxX2NudCwgdG90X2V4ZV9jbnQsIHRvdF9yZXNfY250LCBwcm9wX3Jlc191c2VkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJtYWQiKSwgU0NfRkVfVE9UKQoKIyBleGFtaW5lIHRoZSBwcmVzZW5jZSBvZiAoaGlnaCkgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzCnBsb3QuY29ycmVsYXRpb25zKGxtNl8yLmRhdGEpCgojIGV4Y2x1ZGUgbWFkX3Jlc19jbnQgYXMgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCB0b3RfcmVzX2NudCAod2hpY2ggcHJvdmVkIHNpZ25pZmljYW50KQpsbTZfMi5kYXRhIDwtIGxtNl8yLmRhdGEgJT4lIHNlbGVjdCgtbWFkX3Jlc19jbnQpCmBgYAoKYGBge3J9CmxtNl8yIDwtIGxtKFNDX0ZFX1RPVCB+LiwgZGF0YSA9IGxtNl8yLmRhdGEpCnN1bW1hcnkobG02XzIpCmBgYApOb25lIG9mIHRoZSBNQUQgdmFyaWFibGVzIGlzIHNpZ25pZmljYW50CgoKIyMjIE1vZGVsIDc6IERhaWx5IHRvcGljIGZvY3VzCgpBcyBwcmVkaWN0b3JzLCB1c2UgdG90YWwgbnVtYmVyIG9mIGxlYXJuaW5nIGFjdGlvbnMgKGR1cmluZyBhY3RpdmUgZGF5cykgd2l0aCBhIHBhcnRpY3VsYXIgdG9waWMgZm9jdXM7IHBvc3NpYmxlIHRvcGljIGZvY2k6IAoKKiAnb250b3BpYycgLSB0aGUgdG9waWMgYXNzb2NpYXRlZCB3aXRoIHRoZSBhY3Rpb24gaXMgdGhlIHRvcGljIG9mIHRoZSBjdXJyZW50IHdlZWsKKiAncmV2aXNpdGluZycgLSB0aGUgdG9waWMgYXNzb2NpYXRlZCB3aXRoIHRoZSBhY3Rpb24gaXMgdGhlIHRvcGljIG9mIG9uZSBvZiB0aGUgcHJldmlvdXMgd2Vla3MKKiAnbWV0YWNvZ25pdGl2ZScgLSB0aGUgdG9waWMgYXNzb2NpYXRlZCB3aXRoIHRoZSBhY3Rpb24gaXMgb25lIG9mIHRoZSBmb2xsb3dpbmc6ICdPUkcnLCAnREJPQVJEJywgJ1NUUkFUJywgJ1NUVURZS0lUJwoqICdvcmllbnRlZXJpbmcnIC0gdGhlIHRvcGljIGFzc29jaWF0ZWQgd2l0aCB0aGUgYWN0aW9uIGlzIG9uZSBvZiB0aGUgZm9sbG93aW5nOiAnSE9NRScsICdIT0YnLCAnU0VBUkNIJywgJ1RFQScsICdFWEFNJywgJ1cwMSctJ1cxMycKKiAncHJvamVjdCcgLSB0aGUgYWN0aW9uIGlzIHJlbGF0ZWQgdG8gcHJvamVjdCB3b3JrCgpJbiBhZGRpdGlvbiwgY29uc2lkZXIgaW5jbHVkaW5nIHRoZSBmb2xsb3dpbmcgYmFzaWMgc3RhdGlzdGljczoKIAoqIG1lZGlhbl9YX2NudCAtIG1lZGlhbiBudW1iZXIgb2YgbGVhcm5pbmcgYWN0aW9ucyBwZXIgYWN0aXZlIGRheSB3aXRoIGEgcGFydGljdWxhciB0b3BpYyBmb2N1cyAKKiBtYWRfWF9jbnQgLSBNQUQgb2YgbGVhcm5pbmcgYWN0aW9ucyBwZXIgYWN0aXZlIGRheSB3aXRoIGEgcGFydGljdWxhciB0b3BpYyBmb2N1cwoqIFhfZGF5cyAtIG51bWJlciBvZiBkYXlzIHdpdGggYXQgbGVhc3Qgb25lIGFjdGlvbiB3aXRoIHBhcnRpY3VsYXIgdG9waWMgZm9jdXMgCiogWF9wcm9wIC0gcHJvcG9ydGlvbiBvZiBkYXlzIHdpdGggdGhlIGdpdmVuIHR5cGUgb2YgdG9waWMgZm9jdXMgdmVyc3VzIHRvdGFsIG51bWJlciBvZiBhY3RpdmUgZGF5cwoKTG9hZGluZyB0aGUgcmVxdWlyZWQgZGF0YS4uLgpgYGB7cn0KdG9waWMuc3RhdHMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvdG9waWNfY291bnRzX3N0YXRpc3RpY3NfdzItNV83LTEyLmNzdiIpCiMgc3RyKHRvcGljLnN0YXRzKQoKbG03LmRhdGEgPC0gbWVyZ2UodG9waWMuc3RhdHMsIGV4YW0uc2NvcmVzLCBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFQsIGFsbC55ID0gRikKbG03LmRhdGEgPC0gbG03LmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBTQ19NVF9UT1QpKSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCmBgYAoKIyMjIyBGaXJzdCwgaW5jbHVkZSBvbmx5IHRoZSBpbmRpY2F0b3JzIG9mIGVuZ2FnZW1lbnQgKG5vdCByZWd1bGFyaXR5KQpgYGB7ciByZXN1bHRzPSdoaWRlJ30KbG03XzEuZGF0YSA8LSBsbTcuZGF0YSAlPiUgc2VsZWN0KCBzdGFydHNfd2l0aCgidG90IiksIGVuZHNfd2l0aCgicHJvcCIpLCBTQ19GRV9UT1QpCgpzdW1tYXJ5KGxtN18xLmRhdGEpCgojIGV4YW1pbmUgdGhlIHByZXNlbmNlIG9mIChoaWdoKSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMKcGxvdC5jb3JyZWxhdGlvbnMobG03XzEuZGF0YSkKCiMgZXhjbHVkZSB0b3Rfb3JpZW50X2NudCBhbmQgb3JpbmV0X3Byb3AgYXMgdGhleSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCBzb21lIG90aGVyIHZhcmlhYmxlcwpsbTdfMS5kYXRhIDwtIGxtN18xLmRhdGEgJT4lIHNlbGVjdCgtYyh0b3Rfb3JpZW50X2NudCwgb3JpZW50X3Byb3ApKQpgYGAKCmBgYHtyfQojIGV4Y2x1ZGUgdG90X3Byal9jbnQsIGR1ZSB0byBoaWdoIFZJRgpsbTdfMS5kYXRhIDwtIGxtN18xLmRhdGEgJT4lIHNlbGVjdCgtdG90X3Byal9jbnQpCmxtN18xIDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTdfMS5kYXRhKQpzdW1tYXJ5KGxtN18xKQpgYGAKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIHRvdGFsIG51bWJlciBvZiBwcmVwYXJ0aW9uIChvbnRvcGljKSBldmVudHM6IGFuIGFkZGl0aW9uYWwgb250b3BpYyBldmVudCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC4wMDQ4CiogdG90YWwgbnVtYmVyIG9mIHJldmlzaXRpbmcgZXZlbnRzOiBhbiBhZGRpdGlvbmFsIHJldmlzdGluZyBldmVudCAqZGVjcmVhc2VzKiB0aGUgZmluYWwgZXhhbSBzY29yZSBieSAwLjAwNTIKKiB0b3RhbCBudW1iZXIgb2YgbWV0YWNvZ25pdGl2ZSBldmVudHM6IGFuIGFkZGl0aW9uYWwgbWV0YWNvZ25pdGl2ZSBldmVudCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC4wMTIKKiBwcm9wb3J0aW9uIG9mIGRheXMgd2l0aCBtZXRhY29nbml0aXZlIGV2ZW50cyB2ZXJzdXMgdG90YWwgbnVtYmVyIG9mIGFjdGl2ZSBkYXlzOiBpbmNyZWFzZSBpbiB0aGlzIHByb3BvcnRpb24gKmRlY3JlYXNlcyogdGhlIGV4YW0gc2NvcmUKClItc3F1YXJlZDogMC4xNTYgKGFkanVzdGVkIFIyOiAwLjE0NSkuCgoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG03XzEkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG03XzEpCnBhcihtZnJvdz1jKDEsIDEpKQojIG5vcm1hbGl0eSBpcyBmaW5lCiMgdW5jbGVhciBpZiBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50IGlzIGZ1bGZpbGxlZDsgY2hlY2sgdXNpbmcgdGhpcyBwbG90OgpjaGVjay5ob21vc2NoZWRhc3RpY2l0eShsbTdfMSkKIyBpdCdzIGZpbmUKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMTo4KQogIHByaW50KGNvci50ZXN0KGxtN18xLmRhdGFbLGNdLCBsbTdfMSRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtN18xKQojIG5vdywgaXQncyBmaW5lCmBgYAoKIyMjIyBOb3csIGluY2x1ZGUgYm90aCBpbmRpY2F0b3JzIG9mIGVuZ2FnZW1lbnQgYW5kIGluZGljYXRvciBvZiByZWd1bGFyaXR5CgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBpbmNsdWRlIHRob3NlIGVuZ2FnbWVudCBpbmRpY2F0b3JzIHRoYXQgcHJvdmVkIGF0IGxlYXN0IHNsaWdodGx5IHJlbGV2YW50IGluIHRoZSBwcmV2aW91cyBtb2RlbAojIHBsdXMgbWFkX1hfY250IGFzIGluZGljYXRvcnMgb2YgcmVndWxhcml0eQpsbTdfMi5kYXRhIDwtIGxtNy5kYXRhICU+JSBzZWxlY3QodG90X29udG9waWNfY250LCB0b3RfcmV2aXNpdF9jbnQsIHRvdF9tZXRhY29nX2NudCwgbWV0YWNvZ19wcm9wLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJtYWQiKSwgU0NfRkVfVE9UKQoKIyBleGFtaW5lIHRoZSBwcmVzZW5jZSBvZiAoaGlnaCkgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzCnBsb3QuY29ycmVsYXRpb25zKGxtN18yLmRhdGEpCgojIGV4Y2x1ZGUgdG90X21ldGFjb2dfY250IGFzIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggbWFkX21ldGFjb2dfY250IGFuZCBtYWRfb3JpZW50X2NudApsbTdfMi5kYXRhIDwtIGxtN18yLmRhdGEgJT4lIHNlbGVjdCgtYyh0b3RfbWV0YWNvZ19jbnQsIG1hZF9vcmllbnRfY250KSkKYGBgCgpgYGB7cn0KbG03XzIgPC0gbG0oU0NfRkVfVE9UIH4uLCBkYXRhID0gbG03XzIuZGF0YSkKc3VtbWFyeShsbTdfMikKYGBgClRoZSBvbmx5IHJlZ3VsYXJpdHkgaW5kaWNhdG9yIHRoYXQgcHJvdmVkIHNpZ25pZmljYW50OiBtYWRfb250b3BpY19jbnQgLSBvbmUgdW5pdCBpbmNyZWFzZSBpbiBNQUQgb2Ygb250b3BpYyBjb3VudHMgbGVhZHMgdG8gYSAqZGVjcmVhc2UqIG9mIDAuMTI2IHBvaW50cyBpbiB0aGUgZmluYWwgZXhhbSBzY29yZSAKClIyOiAwLjE0OTMJKGFkanVzdGVkIFIyOiAwLjEzNjYpLgoKCkNoZWNraW5nIGlmIHRoZSBtb2RlbCBzYXRpc2ZpZXMgdGhlIGFzc3VtcHRpb25zIGZvciBsaW5lYXIgcmVncmVzc2lvbjoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgYXNzdW1wdGlvbiAxOiB0aGUgbWVhbiBvZiByZXNpZHVhbHMgaXMgemVybwptZWFuKGxtN18yJHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtN18yKQpwYXIobWZyb3c9YygxLCAxKSkKIyBub3JtYWxpdHkgaXMgZmluZQojIHVuY2xlYXIgaWYgaG9tb3NjZWRhc3RpY2l0eSByZXF1aXJlbWVudCBpcyBmdWxmaWxsZWQ7IGNoZWNrIHVzaW5nIHRoaXMgcGxvdDoKY2hlY2suaG9tb3NjaGVkYXN0aWNpdHkobG03XzIpCiMgbm90IGJhZAoKIyBhIGZldyBpbmZsdWVudGlhbCBwb2ludHM6IDYwLCA1NCwgMjAyCiMgYW5kIGEgZmV3IG91dGxpZXJzOiAxOSwgMjk0LCA1MAoKIyMgYXNzdW1wdGlvbiA0OiBwcmVkaWN0b3JzIGFuZCByZXNpZHVhbHMgYXJlIHVuY29ycmVsYXRlZApmb3IoYyBpbiAxOjcpCiAgcHJpbnQoY29yLnRlc3QobG03XzIuZGF0YVssY10sIGxtN18yJHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG03XzIpCiMgT0sKYGBgCkEgZmV3IG91dGxpZXJzIGFuZCAocG90ZW50aWFsbHkpIGluZmx1ZW50aWFsIHBvaW50czsgYXBhcnQgZnJvbSB0aGF0LCBpdCdzIGZpbmUKCgoKIyMjIE1vZGVsIDg6IFdlZWtseSByZXNvdXJjZSB1c2UgaW5kaWNhdG9ycyAKCkluZGljYXRvcnMgYXJlIGNvbXB1dGVkIGF0IHRoZSB3ZWVrIGxldmVsLCBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIHByaW5jaXBsZTogYSBzY29yZSBvZiBvbmUgaXMgZ2l2ZW4gdG8gYSBzdHVkZW50IChmb3IgYSBnaXZlbiB3ZWVrKSwgaWYgaGUvc2hlIHVzZWQgY2VydGFpbiBraW5kIG9mIHJlc291cmNlIChlLmcuIHZpZGVvKSBtb3JlIHRoYW4gdGhlIGF2ZXJhZ2UgKG1lZGlhbikgdXNlIG9mIHRoZSB0aGF0IHJlc291cmNlIHR5cGUgaW4gdGhlIGdpdmVuIHdlZWsKCkxvYWRpbmcgdGhlIGRhdGEuLi4KYGBge3IgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0V9CnJlcy51c2UuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3Jlc191c2VfaW5kaWNhdG9yc193Mi0xMy5jc3YiKQojc3RyKHJlcy51c2UuaW5kKQoKbG04LmRhdGEgPC0gbWVyZ2UoeCA9IHJlcy51c2UuaW5kLCB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdChVU0VSX0lELCBTQ19GRV9UT1QpLAogICAgICAgICAgICAgICAgICBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCiNzdW1tYXJ5KGxtOC5kYXRhKQoKIyByZW1vdmUgc3R1ZGVudHMgd2hvIGRvIG5vdCBoYXZlIGZpbmFsIGV4YW0gc2NvcmUKbG04LmRhdGEgPC0gbG04LmRhdGEgJT4lIGZpbHRlciggaXMubmEoU0NfRkVfVE9UKT09RkFMU0UgKQoKbG04LmRhdGEgPC0gbG04LmRhdGEgJT4lIHNlbGVjdCgtdXNlcl9pZCkKCiMgZXhhbWltZSBjb3JyZWxhdGlvbnMKcGxvdC5jb3JyZWxhdGlvbnMobG04LmRhdGEpCgojIHZpZGVvX2luZCBhbmQgTUNRX2luZCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIHJlbW92ZSBvbmUgb2YgdGhlbQpsbTguZGF0YSA8LSBsbTguZGF0YSAlPiUgc2VsZWN0KC1WSURFT19pbmQpCmBgYAoKYGBge3J9CmxtOCA8LSBsbShTQ19GRV9UT1QgfiAuLCBkYXRhID0gbG04LmRhdGEpCnN1bW1hcnkobG04KQpgYGAKU2lnbmlmaWNhbnQgcHJlZGljdG9yczoKCiogTUNRX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBpbiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyB1c2Ugb2YgTUNRcyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSB1c2Ugb2YgTUNRIGluIHRoYXQgd2VlayksIGluY3JlYXNlcyB0aGUgZmluYWwgZXhhbSBzY29yZSBieSAwLjk1MSBwb2ludHMgCiogRVhFX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBvZiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyB1c2Ugb2YgZXhlcmNpc2VzIGlzIGhpZ2hlciB0aGFuIHRoZSBhdmVyYWdlIChtZWRpYW4pIHVzZSBvZiBleGVyY2lzZXMgaW4gdGhhdCB3ZWVrKSwgKmRlY3JlYXNlcyogdGhlIGV4YW0gc2NvcmUgYnkgMC44MjUgcG9pbnRzCiogUkVTX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBvZiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyB1c2Ugb2YgcmVhZGluZyBtYXRlcmlhcyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSB1c2Ugb2YgcmVhZGluZyBjb250ZW50IGluIHRoYXQgd2VlayksIGluY3JlYXNlcyB0aGUgZXhhbSBzY29yZSBieSAwLjYwNiBwb2ludHMuCgpSLXNxdWFyZWQ6IDAuMjc0IChhZGp1c3RlZCBSMjogMC4yNjgpLgoKCkNoZWNraW5nIGlmIHRoZSBtb2RlbCBzYXRpc2ZpZXMgdGhlIGFzc3VtcHRpb25zIGZvciBsaW5lYXIgcmVncmVzc2lvbjoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgYXNzdW1wdGlvbiAxOiB0aGUgbWVhbiBvZiByZXNpZHVhbHMgaXMgemVybwptZWFuKGxtOCRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTgpCnBhcihtZnJvdz1jKDEsIDEpKQojIGJvdGggbm9ybWFsaXR5IGFuZCBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50cyBhcmUgZnVsZmlsbGVkCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6NCkKICBwcmludChjb3IudGVzdChsbTguZGF0YVssY10sIGxtOCRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtOCkKIyBpdCdzIGZpbmUKYGBgCgoKIyMjIE1vZGVsIDk6IFRvcGljIGZvY3VzIGluZGljYXRvcnMKCkluZGljYXRvcnMgYXJlIGNvbXB1dGVkIGF0IHRoZSB3ZWVrIGxldmVsLCBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIHByaW5jaXBsZToKYSBzY29yZSBvZiBvbmUgaXMgZ2l2ZW4gdG8gYSBzdHVkZW50IChmb3IgYSBnaXZlbiB3ZWVrKSwgaWYgaGlzL2hlciBudW1iZXIgb2YgZXZlbnRzIHJlbGF0ZWQgdG8gYSBwYXJ0aWN1bGFyIHRvcGljIHR5cGUgKGUuZy4gcmV2aXNpdGluZykgd2FzIGFib3ZlIHRoZSBhdmVyYWdlIChtZWRpYW4pIG51bWJlciBvZiBldmVudHMgd2l0aCB0aGF0IHRvcGljIHR5cGUgaW4gdGhlIGdpdmVuIHdlZWsKCldlZWtzIDYgYW5kIDEzIGFyZSBleGNsdWRlZCBmcm9tIHRoZXNlIGNvbXB1dGF0aW9ucywgYXMgZHVyaW5nIHRoZXNlIHdlZWtzIG9uZSBjYW4gZXhwZWN0ICBkaWZmZXJlbnQgYmVoYXZpb3JhbCBwYXR0ZXJucyB0aGFuIHVzdWFsLgoKTG9hZGluZyB0aGUgZGF0YQpgYGB7ciByZXN1bHRzPSdoaWRlJ30KdG9waWMuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3RvcGljX2Jhc2VkX2luZGljYXRvcnNfdzItNV83LTEyLmNzdiIpCiNzdHIodG9waWMuaW5kKQoKbG05LmRhdGEgPC0gbWVyZ2UoeCA9IHRvcGljLmluZCwgeSA9IGV4YW0uc2NvcmVzICU+JSBzZWxlY3QoVVNFUl9JRCwgU0NfRkVfVE9UKSwKICAgICAgICAgICAgICAgICAgYnkueCA9ICJ1c2VyX2lkIiwgYnkueSA9ICJVU0VSX0lEIiwgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFKQojc3VtbWFyeShsbTkuZGF0YSkKCiMgcmVtb3ZlIHN0dWRlbnRzIHdobyBkbyBub3QgaGF2ZSBmaW5hbCBleGFtIHNjb3JlCmxtOS5kYXRhIDwtIGxtOS5kYXRhICU+JSBmaWx0ZXIoIGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICkKCmxtOS5kYXRhIDwtIGxtOS5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpCgpwbG90LmNvcnJlbGF0aW9ucyhsbTkuZGF0YSkKCiMgb3JpZW50X2luZCBhbmQgbWV0YWNvZ19pbmQgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCByZW1vdmUgb25lIG9mIHRoZW0KbG05LmRhdGEgPC0gbG05LmRhdGEgJT4lIHNlbGVjdCgtb3JpZW50X2luZCkKYGBgCgpgYGB7cn0KbG05IDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTkuZGF0YSkKc3VtbWFyeShsbTkpCmBgYApTaWduaWZpY2FudCBwcmVkaWN0b3JzOgoKKiBvbnRvcGljX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBpbiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyBudW1iZXIgb2YgJ29udG9waWMnIGV2ZW50cyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSBudW1iZXIgb2YgJ29udG9waWMnIGV2ZW50cyBpbiB0aGF0IHdlZWspLCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC45MzAzIHBvaW50cyAKKiBtZXRhY29nX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBpbiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyBudW1iZXIgb2YgJ21ldGFjb2duaXRpdmUnIGV2ZW50cyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSBudW1iZXIgb2YgJ21ldGFjb2duaXRpdmUnIGV2ZW50cyBpbiB0aGF0IHdlZWspLCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC40NjA2IHBvaW50cwoKUi1zcXVhcmVkOiAwLjE0ODQJKGFkanVzdGVkIFItc3F1YXJlZDogMC4xNDEyKQoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG05JHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtOSkKcGFyKG1mcm93PWMoMSwgMSkpCiMgYm90aCBub3JtYWxpdHkgYW5kIGhvbW9zY2VkYXN0aWNpdHkgcmVxdWlyZW1lbnRzIGFyZSBmdWxmaWxsZWQKIyB0aGVyZSBhcmUgZmV3IG91dGxpZXJzICgyMDIsIDIxMywgMzY1KSwgYnV0IG5vIGluZmx1ZW50aWFsIHBvaW50cwoKIyBjaGVjayB0aGUgb3V0bGllcnMKbG05LmRhdGFbYygyMDIsMjEzLDM2NSksXQojIHZlcnkgaW50ZXJlc3Rpbmc6CiMgLSAyMDIgd2FzIGhpZ2hseSBhY3RpdmUgYnV0IGVuZGVkIHVwIHdpdGggemVybyBmaW5hbCBleGFtIHNjb3JlCiMgLSAyMTMgYW5kIDM2NSB3ZXJlIG5vdCBwcmVwYXJpbmcgZm9yIGxlY3R1cmVzLCBhbmQgZ2VuZXJhbHkgaGFkIGxvdyBlbmdhZ2VtZW50LCBidXQgZGlkIHRoZSBleGFtIGV4Y2VsbGVudGx5CgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6NCkKICBwcmludChjb3IudGVzdChsbTkuZGF0YVssY10sIGxtOSRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtOSkKIyBpdCdzIGZpbmUKYGBgCgoKIyMjIE1vZGVsIDEwOiBDb21iaW5lIHJlc291cmNlIHVzZSBhbmQgdG9waWMgZm9jdXMgaW5kaWNhdG9ycwoKVXNlIHRob3NlIGluZGljYXRvcnMgdGhhdCBwcm92ZWQgc2lnbmlmaWNhbnQgaW4gdGhlIHByZXZpb3VzIHR3byBtb2RlbHMgKG1vZGVscyA4IGFuZCA5KQoKTG9hZGluZyB0aGUgZGF0YQpgYGB7ciByZXN1bHRzPSdoaWRlJ30KdG9waWMuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3RvcGljX2Jhc2VkX2luZGljYXRvcnNfdzItNV83LTEyLmNzdiIpCnJlcy51c2UuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3Jlc191c2VfaW5kaWNhdG9yc193Mi0xMy5jc3YiKQoKbG0xMC5kYXRhIDwtIG1lcmdlKHggPSB0b3BpYy5pbmQgJT4lIHNlbGVjdCh1c2VyX2lkLCBvbnRvcGljX2luZCwgbWV0YWNvZ19pbmQpLCAKICAgICAgICAgICAgICAgICAgIHkgPSByZXMudXNlLmluZCAlPiUgc2VsZWN0KHVzZXJfaWQsIE1DUV9pbmQsIFJFU19pbmQsIEVYRV9pbmQpLAogICAgICAgICAgICAgICAgICBieSA9ICJ1c2VyX2lkIiwgYWxsID0gVFJVRSkKCmxtMTAuZGF0YSA8LSBtZXJnZSh4ID0gbG0xMC5kYXRhLCB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdChVU0VSX0lELCBTQ19GRV9UT1QpLAogICAgICAgICAgICAgICAgICBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCnN1bW1hcnkobG0xMC5kYXRhKQoKIyByZW1vdmUgc3R1ZGVudHMgd2hvIGRvIG5vdCBoYXZlIGZpbmFsIGV4YW0gc2NvcmUKbG0xMC5kYXRhIDwtIGxtMTAuZGF0YSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCgpwbG90LmNvcnJlbGF0aW9ucyhsbTEwLmRhdGEgJT4lIHNlbGVjdCgtdXNlcl9pZCkpCgojIFJFU19pbmQgYW5kIG1ldGFjb2dfaW5kIGFyZSBoaWdobHkgY29ycmVsYXRlZCwgcmVtb3ZlIG1ldGFjb2dfaW5kIGFzIGl0IHdhcyBsZXNzIHNpZ25pZmljYW50IGluIHRoZSBwcmV2aW91cyBtb2RlbHMKbG0xMC5kYXRhIDwtIGxtMTAuZGF0YSAlPiUgc2VsZWN0KC1tZXRhY29nX2luZCkKYGBgCgpgYGB7cn0KbG0xMCA8LSBsbShTQ19GRV9UT1QgfiAuLCBkYXRhID0gbG0xMC5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpKQpzdW1tYXJ5KGxtMTApCmBgYApBbGwgNCBwcmVkaWN0b3JzIGFyZSBzaWduaWZpY2FudDsgaG93ZXZlciwgb25seSBzbGlnaHQgaW1wcm92ZW1lbnQgaW4gUjIgdy5yLnQuIG1vZGVsIDg6ClItc3F1YXJlZDogMC4yODEyCShhZGp1c3RlZCBSLXNxdWFyZWQ6IDAuMjc1MSkKCgojIyMgTW9kZWwgMTE6IEV4dGVuZCBtb2RlbCAxMCB3aXRoIHRvdGFsIG51bWJlciBvZiBzdHVkeSBzZXNzaW9ucyBhbmQgZW50cm9weSBvZiB3ZWVrbHkgc3R1ZHkgc2Vzc2lvbiBjb3VudHMKCkxvYWRpbmcgdGhlIGRhdGEKYGBge3J9CndlZWtseS5zZXNzaW9ucyA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS93ZWVrbHlfc2Vzc2lvbl9wcm9wcy5jc3YiKQoKbG0xMS5kYXRhIDwtIG1lcmdlKHggPSBsbTEwLmRhdGEsIHkgPSB3ZWVrbHkuc2Vzc2lvbnMgJT4lIHNlbGVjdCh1c2VyX2lkLCBzX3RvdGFsLCB3ZWVrbHlfZW50cm9weSksCiAgICAgICAgICAgICAgICAgICBieSA9ICd1c2VyX2lkJywgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFKQojc3VtbWFyeShsbTExLmRhdGEpCmxtMTEuZGF0YSA8LSBsbTExLmRhdGFbLGMoMTo1LDcsOCw2KV0KCnBsb3QuY29ycmVsYXRpb25zKGxtMTEuZGF0YSAlPiUgc2VsZWN0KC11c2VyX2lkKSkKCiMgdG90YWwgbnVtYmVyIG9mIHNlc3Npb25zIGlzIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggYWxtb3N0IGFsbCBvdGhlciB2YXJpYWJsZXMKbG0xMS5kYXRhIDwtIGxtMTEuZGF0YSAlPiUgc2VsZWN0KC1zX3RvdGFsKQpgYGAKCmBgYHtyfQpsbTExIDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTExLmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBvbnRvcGljX2luZCkpKQpzdW1tYXJ5KGxtMTEpCmBgYApBbGwgNCBwcmVkaWN0b3JzIHRoYXQgd2VyZSBldmVudHVhbGx5IHVzZWQgZm9yIG1vZGVsIGJ1aWxkaW5nIC0gTUNRX2luZCwgRVhFX2luZCwgUkVTX2luZCwgYW5kIHdlZWtseV9lbnRyb3B5IC0gcHJvdmVkIGhpZ2hseSBzaWduaWZpY2FudC4KClItc3F1YXJlZDogMC4zMjU1CShhZGp1c3RlZCBSLXNxdWFyZWQ6IDAuMzE5OCkKCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTExJHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtMTEpCnBhcihtZnJvdz1jKDEsIDEpKQojIG5vcm1hbGl0eSBpcyBmdWxmaWxsZWQsIGJ1dCB0aGUgaG9tb3NjZWRhc3RpY2l0eSByZXF1aXJlbWVudHMgaXMgcXVlc3Rpb25hYmxlCmNoZWNrLmhvbW9zY2hlZGFzdGljaXR5KGxtMTEpCiMgbm90IGdvb2QsIHRoZXJlIG91dGxpZXJzIGFuZC9vciBpbmZsdWVudGlhbCBwb2ludHMKCiMgY2hlY2sgdGhlIG91dGxpZXJzCmxtMTEuZGF0YVtjKDQ5LDI5NCw1MCksXQojIC0gMjk0IGFuZCA1MDogbG93IHRvIG1vZGVyYXRlIGFjdGl2aXR5IGluZGljYXRvcnMgYW5kIHplcm8gZmluYWwgZXhhbSBzY29yZQojIC0gMjMwOiBtb2RlcmF0ZSBhY3Rpdml0eSBpbmRpY2F0b3JzLCBidXQgZXhjZWxsZW50IGV4YW0gc2NvcmUKCiMgY2hlY2sgaW5mbHVlbnRpYWwgcG9pbnRzCmluZi5pbmRpY2VzIDwtIGFzLm51bWVyaWMobmFtZXMoaGVhZChzb3J0KGNvb2tzLmRpc3RhbmNlKGxtMTEpLCBkZWNyZWFzaW5nID0gVCkpKSkKbG0xMS5kYXRhW2luZi5pbmRpY2VzLF0KIyBvYnNlcnZhdGlvbnMgd2l0aCBvcmRpbmFsIG51bWJlcnMgIDE2MywgMjQxLCAzMzYgc2hvdWxkIGJlIGNvbnNpZGVyZWQgZm9yIHJlbW92YWwKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMTo1KQogIHByaW50KGNvci50ZXN0KGxtMTEuZGF0YVssY10sIGxtMTEkcmVzaWR1YWxzKSkKIyBPSwoKIyMgYXNzdW1wdGlvbiA2OiBubyBtdWx0aWNvbGluZWFyaXR5IGJldHdlZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzCnZpZihsbTExKQojIyBvbnRvcGljX2luZCBhbmQgTUNRX2luZCBoYXZlIHZhbHVlcyA+IDI7IHJlbW92ZSBvbnRvcGljX2luZCBhcyBpdCBpcyBub3Qgc2lnbmlmaWNhbnQKIyBub3cgKGFmdGVyIG9udG9waWNfaW5kIHdhcyByZW1vdmVkKSwgaXQncyBmaW5lCmBgYApJZiB0aGUgbW9kZWwgaXMgdG8gYmUgdXNlZCwgdGhlIG91dGxpZXJzIHNob3VsZCBiZSBkZWFsdCB3aXRoLgoKCiMjIyBNb2RlbCAxMjogRXh0ZW5kIG1vZGVsIDEwIHdpdGggbnVtYmVyIG9mIHN0dWR5IHNlc3Npb25zIHBlciB3ZWVrZGF5LCBhbmQgd2Vla2RheSBlbnRyb3B5IG9mIHN0dWR5IHNlc3Npb24gY291bnRzCgpJbiBhZGRpdGlvbiB0byBwcmVkaWN0b3JzIGZyb20gTW9kZWwgMTAgYW5kIHdlZWtkYXkgZW50cm9weSBvZiBzdHVkeSBzZXNzaW9uIGNvdW50cywgdXNlLCBhcyBwcmVkaWN0b3JzLCBzdHVkeSBzZXNzaW9uIGNvdW50cyBmb3IgdGhvc2Ugd2VlayBkYXlzIHRoYXQgcHJvdmVkIGFzIHNpZ25pZmljYW50IHByZWRpY3RvcnMgaW4gTW9kZWwgNSAoTW9uLCBUdWUsIFdlZCwgVGh1KS4KCkxvYWRpbmcgdGhlIGRhdGEuLi4KYGBge3J9CndlZWtkYXkuc2Vzc2lvbnMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvd2Vla2RheV9zZXNzaW9uX3Byb3BzLmNzdiIpCgpsbTEyLmRhdGEgPC0gbWVyZ2UoeCA9IGxtMTAuZGF0YSwKICAgICAgICAgICAgICAgICAgIHkgPSB3ZWVrZGF5LnNlc3Npb25zICU+JSBzZWxlY3QodXNlcl9pZCwgTW9uX2NvdW50LCBUdWVfY291bnQsIFdlZF9jb3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGh1X2NvdW50LCB3ZWVrZGF5X2VudHJvcHkpLCAKICAgICAgICAgICAgICAgICAgIGJ5ID0gInVzZXJfaWQiLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCgojIHN1bW1hcnkobG0xMi5kYXRhKQpsbTEyLmRhdGEgPC0gbG0xMi5kYXRhWyxjKDE6NSw3OjExLDYpXQoKcGxvdC5jb3JyZWxhdGlvbnMobG0xMi5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpKQpgYGAKCmBgYHtyfQpsbTEyIDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTEyLmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBvbnRvcGljX2luZCwgUkVTX2luZCkpKQpzdW1tYXJ5KGxtMTIpCmBgYAoKCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTEyJHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtMTIpCnBhcihtZnJvdz1jKDEsIDEpKQojIGJvdGggbm9ybWFsaXR5IGFuZCBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50cyBhcmUgZnVsZmlsbGVkCgojIHRoZXJlIGFyZSBmZXcgb3V0bGllcnMgKDIzMCwgNTAsIDQ1OSksIGJ1dCB0aGV5IGRvIG5vdCBsb29rIHRoYXQgc2lnbmlmaWNhbnQKCiMgY2hlY2sgaW5mbHVlbnRpYWwgcG9pbnRzCmluZi5pbmRpY2VzIDwtIGFzLm51bWVyaWMobmFtZXMoaGVhZChzb3J0KGNvb2tzLmRpc3RhbmNlKGxtMTIpLCBkZWNyZWFzaW5nID0gVCkpKSkKbG0xMi5kYXRhW2luZi5pbmRpY2VzLF0KIyBvYnNlcnZhdGlvbnMgd2l0aCBvcmRpbmFsIG51bWJlcnMgIDQ1OSwgMjIsIDI5MiBzaG91bGQgYmUgY29uc2lkZXJlZCBmb3IgcmVtb3ZhbAoKIyMgYXNzdW1wdGlvbiA0OiBwcmVkaWN0b3JzIGFuZCByZXNpZHVhbHMgYXJlIHVuY29ycmVsYXRlZApmb3IoYyBpbiAxOjEwKQogIHByaW50KGNvci50ZXN0KGxtMTIuZGF0YVssY10sIGxtMTIkcmVzaWR1YWxzKSkKIyBPSwoKIyMgYXNzdW1wdGlvbiA2OiBubyBtdWx0aWNvbGluZWFyaXR5IGJldHdlZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzCnZpZihsbTEyKQojIyBvbnRvcGljX2luZCBoYXMgdmFsdWUgPiAyOyByZW1vdmUgaXQKIyMgbm93IFJFU19pbmQgc3RhbmRzIG91dCB3aXRoIGhpZ2ggdmFsdWU7IHJlbW92ZSBpdAojIG5vdyAoYWZ0ZXIgb250b3BpY19pbmQgYW5kIFJFU19pbmQgd2VyZSByZW1vdmVkKSwgaXQncyBmaW5lCmBgYAoKCgojIyMgTW9kZWwgMTM6IEV4dGVuZHMgbW9kZWwgMTIgd2l0aCBlbnRyb3B5IG9mIHdlZWtseSBzdHVkeSBzZXNzaW9uIGNvdW50cwoKTG9hZGluZyB0aGUgZGF0YS4uLgpgYGB7cn0Kd2Vla2x5LnNlc3Npb25zIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3dlZWtseV9zZXNzaW9uX3Byb3BzLmNzdiIpCgpsbTEzLmRhdGEgPC0gbWVyZ2UoeCA9IGxtMTIuZGF0YSAlPiUgc2VsZWN0KC1jKG9udG9waWNfaW5kLCBSRVNfaW5kKSksIAogICAgICAgICAgICAgICAgICAgeSA9IHdlZWtseS5zZXNzaW9ucyAlPiUgc2VsZWN0KHVzZXJfaWQsIHdlZWtseV9lbnRyb3B5KSwKICAgICAgICAgICAgICAgICAgIGJ5ID0gJ3VzZXJfaWQnLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCiNzdW1tYXJ5KGxtMTMuZGF0YSkKbG0xMy5kYXRhIDwtIGxtMTMuZGF0YVssYygxOjgsMTAsOSldCgpwbG90LmNvcnJlbGF0aW9ucyhsbTEzLmRhdGEgJT4lIHNlbGVjdCgtdXNlcl9pZCkpCmBgYAoKYGBge3J9CmxtMTMgPC0gbG0oU0NfRkVfVE9UIH4gLiwgZGF0YSA9IGxtMTMuZGF0YSAlPiUgc2VsZWN0KC11c2VyX2lkKSkKc3VtbWFyeShsbTEzKQpgYGAKClItc3F1YXJlZDogMC4zNDkgKGFkanVzdGVkIFItc3F1YXJlZDogMC4zMzgpCgoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG0xMyRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTEzKQpwYXIobWZyb3c9YygxLCAxKSkKY2hlY2suaG9tb3NjaGVkYXN0aWNpdHkobG0xMykKIyBub3JtYWxpdHkgaXMgZnVsZmlsbGVkCiMgaG9tb3NjZWRhc3RpY2l0eSBpcyBzb21ld2hhdCBxdWVzdGlvbmFibGUgLSB0aGVyZSBhcmUgb3V0bGllcnMgYW5kL29yIGluZmx1ZW50aWFsIHBvaW50cwoKIyBvdXRsaWVyczogMjMwLCA1MCwgNDU5CgojIGNoZWNrIGluZmx1ZW50aWFsIHBvaW50cwppbmYuaW5kaWNlcyA8LSBoZWFkKHNvcnQoY29va3MuZGlzdGFuY2UobG0xMyksIGRlY3JlYXNpbmcgPSBUKSkKaW5mLmluZGljZXMKbG0xMy5kYXRhW2FzLm51bWVyaWMobmFtZXMoaW5mLmluZGljZXMpKSxdCiMgb2JzZXJ2YXRpb25zIHdpdGggb3JkaW5hbCBudW1iZXJzICA0NTksIDE2MywgMzM2LCBhbmQgMjQxIHNob3VsZCBiZSBjb25zaWRlcmVkIGZvciByZW1vdmFsIChhbGwgaGF2ZSBmaW5hbCBleGFtIHNjb3JlID0gemVybykKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMToxMCkKICBwcmludChjb3IudGVzdChsbTEzLmRhdGFbLGNdLCBsbTEzJHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG0xMykKIyBpdCdzIGZpbmUKYGBg